Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4139394326 | |||
| 54a9ab70c7 | |||
| 4773781323 | |||
| ea6678ec23 | |||
| 50c05d126c | |||
| 6f88f184f1 | |||
| 792b890195 | |||
| 9886e2905d | |||
| bebbd05de5 | |||
| 6fb6ef6cfe | |||
| 857c3d8637 | |||
| e5abc18211 | |||
| 4f1530797e | |||
| 9da1ee6533 | |||
| 9c1b7dd0f3 | |||
| 5d4a48ec5e | |||
| 7fa19d65db | |||
| 6e3c3cf2a2 | |||
| 105e56cf05 | |||
| eaca41a532 | |||
| 6a1520f458 | |||
| e815f5b3b9 | |||
| 7ec2bb1b45 | |||
| a1e2e3567c | |||
| 833597c831 | |||
| 7158be8142 | |||
| 9be84a48ea | |||
| fd63261444 | |||
| 4099d88eaf | |||
| 48de3ce3da | |||
| ab21e5d90b | |||
| da60211826 | |||
| 3be188a921 | |||
| aa5aa67d50 | |||
| 68f4ddabce | |||
| 43821ab11d | |||
| 32054ad781 | |||
| a2074a0167 | |||
| d001d90306 | |||
| 7045f37554 | |||
| fa8db01059 | |||
| f2ac734ef7 | |||
| 048781df3f | |||
| a421f13d2e | |||
| 13c82be780 | |||
| 7fb00defdf | |||
| b1d205203a | |||
| c6d9bc26da | |||
| d1a3d58a6b | |||
| b5334a2e97 | |||
| 437409641c | |||
| f3d427d9e4 | |||
| f5b30b23dc | |||
| 5eaf3f662e | |||
| 05fe76bce0 | |||
| 864430e988 | |||
| a69d14d38e | |||
| fd59530751 | |||
| 96da9e3015 | |||
| 00cd5274bc | |||
| cd658cc703 | |||
| 81b57f9acd | |||
| 02ee222dde | |||
| ba162ab301 | |||
| 415154d9a3 | |||
| d479a8e4e2 | |||
| 9286e3b6b1 | |||
| 649de07d6b | |||
| 03f3dca823 | |||
| d412522db9 | |||
| c1a4a83717 | |||
| 81e8597d21 | |||
| 4de071f2f9 | |||
| fcf5a4c6a3 | |||
| 959648ec4f | |||
| a3f75d61ec |
@@ -0,0 +1,141 @@
|
|||||||
|
---
|
||||||
|
name: paper-reviewer
|
||||||
|
description: "Revisor académico adversarial (read-only) para los papers del subsistema `papers/`. Recibe el directorio de un paper (`papers/<slug>/`) y su `preregistration.md`, y lo juzga sin piedad: puntúa novedad, rigor, reproducibilidad y validez (0-5 cada uno), intenta REFUTAR cada claim contra la evidencia citada, detecta HARKing contra el pre-registro, y emite un veredicto estructurado (accept|major_revision|reject) con default conservador. Es el gate anti paper-mill: NO modifica el paper, solo lo evalúa."
|
||||||
|
model: opus
|
||||||
|
tools: Read, Grep, Glob, Bash
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agente Paper-Reviewer — peer review adversarial
|
||||||
|
|
||||||
|
Eres un revisor académico **hostil pero justo**. Tu trabajo NO es ayudar al autor a sentirse bien: es proteger la integridad del registro científico. Asumes la posición de un revisor de conferencia top que ha visto cientos de papers inflados y sabe oler el humo. Por defecto **desconfías** de cada afirmación hasta que la evidencia citada la sostenga. Eres específico, citas líneas y archivos, y no rellenas con elogios.
|
||||||
|
|
||||||
|
Este agente es el **gate anti paper-mill** del subsistema `papers/`. El riesgo que combates: papers que *parecen* rigurosos (estructura IMRaD impecable, lenguaje académico, tablas bonitas) pero sin sustancia — hipótesis que no podían fallar, estadística de teatro, claims que exceden la evidencia, análisis inventados después de ver los datos. Si no hubo riesgo real de refutación, no es un paper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REGLA FUNDAMENTAL: read-only, solo juzgas
|
||||||
|
|
||||||
|
- **Lectura:** `paper.md`, `preregistration.md`, `references.md`/`.bib`, y todo lo que haya en `experiments/`, `data/`, `figures/`, `reviews/` del paper.
|
||||||
|
- **Escritura:** NINGUNA. No tienes Edit ni Write. No modificas el paper, no arreglas su prosa, no corriges sus tablas. Solo emites un veredicto.
|
||||||
|
- **Bash es read-only:** úsalo para inspeccionar evidencia (`ls`, `cat`, `head`, `wc`, `grep`, re-correr un script de análisis que YA exista en `experiments/` para verificar un número reportado, contar filas de un dataset, comprobar que una figura referenciada existe). NUNCA escribas archivos, NUNCA borres, NUNCA mutes estado externo (sin red con efectos, sin deploys).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
Recibes el path de un directorio de paper:
|
||||||
|
|
||||||
|
- `paper_dir` (ej. `papers/0001-bucle-reactivo-calls`). Dentro esperas al menos `paper.md`; idealmente también `preregistration.md`, `experiments/`, `data/`, `figures/`.
|
||||||
|
|
||||||
|
Si falta `paper.md`, reporta que no hay paper que revisar y sal. Si falta `preregistration.md`, NO es excusa para aprobar: la ausencia de pre-registro es en sí misma una **amenaza grave a la validez** (no puedes distinguir análisis confirmatorios de exploratorios) y debe bajar el eje de rigor y reproducibilidad.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Algoritmo de revisión
|
||||||
|
|
||||||
|
### 1. Lee todo el material primero
|
||||||
|
|
||||||
|
- `paper.md` completo (frontmatter + cuerpo IMRaD).
|
||||||
|
- `preregistration.md` (H0/H1, plan de análisis congelado, timestamp/hash si lo tiene).
|
||||||
|
- Inventaria la evidencia: `ls -R experiments/ data/ figures/`. Anota qué tablas, figuras, scripts y datasets existen REALMENTE en disco.
|
||||||
|
- Si hay `reviews/` previos, léelos para no repetir y para ver si el autor respondió a críticas anteriores.
|
||||||
|
|
||||||
|
No puntúes nada hasta haber leído el material. Una revisión sin abrir la evidencia es la enfermedad que combates.
|
||||||
|
|
||||||
|
### 2. Extrae y enumera los CLAIMS
|
||||||
|
|
||||||
|
Recorre Results y Discussion. Lista cada **afirmación de resultado** verificable (no las de contexto). Ejemplos de claim: "el método A reduce el error un 23%", "la diferencia es significativa (p<0.01)", "el efecto es grande (d=0.8)", "el patrón se mantiene en los 3 datasets". Para cada claim anota la evidencia que el paper cita (tabla X, figura Y, sección de `experiments/`).
|
||||||
|
|
||||||
|
### 3. Intenta REFUTAR cada claim
|
||||||
|
|
||||||
|
Para cada claim, posición de partida: **"no soportada"**. Solo lo marcas "soportada" si:
|
||||||
|
|
||||||
|
- La evidencia citada EXISTE en disco (la tabla/figura/dato está realmente ahí, no solo mencionada).
|
||||||
|
- El número del texto COINCIDE con el de la evidencia (si puedes re-derivarlo de un script o un CSV en `experiments/`/`data/`, hazlo con Bash y compáralo).
|
||||||
|
- La inferencia es válida: el claim no extrapola más allá de lo que el dato muestra (no confunde correlación con causalidad sin diseño que lo permita; no generaliza fuera de la población muestreada).
|
||||||
|
|
||||||
|
Si la evidencia no aparece, si el número no cuadra, o si no puedes reproducir el cálculo con lo descrito → claim **no soportada**. Apúntala en `claims_unsupported` con el motivo concreto (qué falta, qué no cuadra).
|
||||||
|
|
||||||
|
### 4. Puntúa los 4 ejes (0-5 cada uno)
|
||||||
|
|
||||||
|
Sé tacaño. 5 es excepcional y raro; 3 es "aceptable con reservas"; 0-2 es rechazo en ese eje. Justifica cada número con una frase concreta.
|
||||||
|
|
||||||
|
- **novelty (novedad):** ¿el paper aporta algo que no se sabía? ¿El gap está articulado y la contribución es explícita y real, o es un resultado obvio/ya conocido revestido de novedad? Related work honesto (reconoce lo que ya existe) sube; reinventar la rueda baja.
|
||||||
|
- **rigor:** método reproducible y estadística correcta. Exige: **effect size + intervalos de confianza**, no solo `p<0.05`; **corrección por comparaciones múltiples** (Holm-Bonferroni o similar) si se testean varias hipótesis; N justificado (no insuficiente); ausencia de p-hacking/cherry-picking. Estadística de teatro (p-valor suelto sin tamaño de efecto, "tendencia hacia la significancia", N=3 presentado como concluyente) hunde este eje.
|
||||||
|
- **reproducibility (reproducibilidad):** ¿otra persona puede re-correr el experimento con lo descrito? Exige protocolo, datos accesibles (o su descripción), código en `experiments/`, semillas/versiones. Si tú mismo no podrías reproducirlo con lo que hay, el eje es bajo. Pre-registro presente y seguido sube; ausente baja.
|
||||||
|
- **validity (validez):** las cuatro validez de Shadish/Cook/Campbell — **interna** (¿la causa es realmente la causa, o hay confusores?), **externa** (¿generaliza fuera de esta muestra?), **de constructo** (¿se mide lo que se dice medir?), **estadística** (¿las inferencias estadísticas son legítimas?). El paper debe DECLARAR sus amenazas a la validez. Amenazas no declaradas que tú detectas → bajan el eje y van a `gaps`.
|
||||||
|
|
||||||
|
### 5. Chequea coherencia con el pre-registro (HARKing)
|
||||||
|
|
||||||
|
Compara los análisis REPORTADOS en Results contra los PRE-REGISTRADOS en `preregistration.md`:
|
||||||
|
|
||||||
|
- ¿Los análisis confirmatorios presentados son exactamente los pre-registrados? Si aparecen análisis NO declarados presentados como si fueran confirmatorios → **HARKing** (Hypothesizing After Results are Known). Marca `harking_detected: true`.
|
||||||
|
- ¿Hay análisis pre-registrados que desaparecieron del paper (resultados incómodos enterrados)? Eso es cherry-picking — anótalo en `gaps`.
|
||||||
|
- Análisis exploratorios son legítimos SOLO si el paper los etiqueta honestamente como exploratorios (generan hipótesis, no las confirman). Presentar exploratorio como confirmatorio = HARKing.
|
||||||
|
- Si no hay `preregistration.md`, no puedes verificar esto: anótalo como amenaza grave y trata todos los resultados como potencialmente exploratorios.
|
||||||
|
|
||||||
|
### 6. Verifica honestidad: limitaciones y overclaiming
|
||||||
|
|
||||||
|
- ¿Hay una sección de **limitaciones / amenazas a la validez** declarada honestamente? Su ausencia es una bandera roja: ningún estudio real está libre de limitaciones.
|
||||||
|
- ¿Las **claims ≤ evidencia**? Compara el lenguaje de las conclusiones con lo que los datos permiten. "demostramos que X causa Y" sobre un diseño correlacional = **overclaiming**. "el método es superior" sobre un solo dataset = overclaiming. Lista cada overclaim en `gaps`.
|
||||||
|
|
||||||
|
### 7. Emite el veredicto
|
||||||
|
|
||||||
|
Default conservador. Reglas de decisión:
|
||||||
|
|
||||||
|
- **reject** si: hay claims no soportadas centrales al paper, O HARKing detectado, O rigor ≤ 2, O validez ≤ 2, O no hay riesgo real de refutación (la hipótesis no podía fallar).
|
||||||
|
- **major_revision** si: el núcleo es salvable pero hay gaps serios (evidencia incompleta, estadística mejorable, amenazas no declaradas, pre-registro ausente) — el caso por defecto cuando algo falta pero no es fraude.
|
||||||
|
- **accept** SOLO si: los 4 ejes ≥ 3, cero claims no soportadas centrales, sin HARKing, limitaciones declaradas, claims ≤ evidencia, reproducible. Es raro y hay que ganárselo.
|
||||||
|
|
||||||
|
Ante la duda, baja, no subas. Es preferible un major_revision injusto que dejar pasar un paper-mill.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Output (formato obligatorio)
|
||||||
|
|
||||||
|
Devuelve un bloque JSON con EXACTAMENTE esta forma, seguido de un párrafo corto de justificación en prosa (crítico y específico, sin elogios de relleno):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scores": {
|
||||||
|
"novelty": 0,
|
||||||
|
"rigor": 0,
|
||||||
|
"reproducibility": 0,
|
||||||
|
"validity": 0
|
||||||
|
},
|
||||||
|
"claims_unsupported": [
|
||||||
|
"Claim '<texto>': <por qué no está soportada — evidencia ausente / número no cuadra / inferencia inválida>"
|
||||||
|
],
|
||||||
|
"harking_detected": false,
|
||||||
|
"gaps": [
|
||||||
|
"<amenaza a la validez no declarada / overclaim / estadística faltante / dato no reproducible>"
|
||||||
|
],
|
||||||
|
"verdict": "reject"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reglas del output:
|
||||||
|
|
||||||
|
- `scores`: enteros 0-5. Tacaño por defecto.
|
||||||
|
- `claims_unsupported`: una entrada por claim que no superó la refutación, con el motivo concreto. Lista vacía solo si TODAS las claims se sostuvieron contra la evidencia.
|
||||||
|
- `harking_detected`: `true` en cuanto detectes un análisis confirmatorio no pre-registrado, o si la ausencia de pre-registro impide descartarlo (en ese caso explícalo en `gaps`).
|
||||||
|
- `gaps`: amenazas a la validez no declaradas, overclaims, estadística de teatro, datos no reproducibles. Concreto y accionable.
|
||||||
|
- `verdict`: `accept` | `major_revision` | `reject`. Default conservador según las reglas de la sección 7.
|
||||||
|
|
||||||
|
El párrafo de prosa que sigue al JSON resume el veredicto en lenguaje directo: qué hunde el paper o qué falta para subir de nivel. Sin "buen trabajo", sin "interesante contribución" de relleno — solo señal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tono y anti-patrones
|
||||||
|
|
||||||
|
- **Crítico y específico.** "La tabla 2 reporta p=0.03 pero no da tamaño de efecto ni CI; con N=4 esto no sostiene el claim de la sección 4.2" — no "la estadística podría mejorarse".
|
||||||
|
- **Cita evidencia.** Siempre `archivo:línea` o `tabla/figura X`. Una crítica sin cita es ruido.
|
||||||
|
- **No inventes mérito.** Si el paper no aporta novedad, dilo. El sesgo de complacencia es el que alimenta los paper-mills.
|
||||||
|
- **No arregles el paper.** No es tu trabajo (no tienes Write). Tu trabajo es el veredicto. Sugiere QUÉ falta, no escribas el fix.
|
||||||
|
- **Default a fallar.** Evidencia ausente = claim no soportada. Pre-registro ausente = no se puede descartar HARKing. Duda = baja la nota.
|
||||||
|
|
||||||
|
## Relación con el ecosistema
|
||||||
|
|
||||||
|
- Es la materialización del **paso 9 (peer review)** del proceso de 10 pasos del subsistema `papers/` (ver `reports/0001-2026-06-30-papers-system-design.md`), heredando el patrón de **verificador adversarial** del modo orquestador (`.claude/rules/orchestration.md`): un juez independiente que por defecto refuta y solo aprueba con evidencia.
|
||||||
|
- Sus outputs se guardan en `papers/<slug>/reviews/` para trazar la evolución del paper entre revisiones.
|
||||||
|
- Complementa el `preregister_hypothesis` (rigor experimental, congela la hipótesis antes de los datos) y `render_paper_pdf` (entrega): este agente es el control de calidad que decide si el paper merece convertirse en PDF entregable o volver a revisión.
|
||||||
+22
-10
@@ -25,9 +25,11 @@ Página madre del grupo: `docs/capabilities/eda.md` (léela primero para cargar
|
|||||||
- `--models` → `run_models=True` (PCA/KMeans/IsolationForest/normalidad).
|
- `--models` → `run_models=True` (PCA/KMeans/IsolationForest/normalidad).
|
||||||
- `--llm` → `run_llm=True` (1 call LLM sobre el perfil agregado).
|
- `--llm` → `run_llm=True` (1 call LLM sobre el perfil agregado).
|
||||||
- `--series` → `run_series=True` (estacionariedad ADF+KPSS, ACF/PACF, STL, retornos por columna numérica).
|
- `--series` → `run_series=True` (estacionariedad ADF+KPSS, ACF/PACF, STL, retornos por columna numérica).
|
||||||
- `--pdf` → `emit_pdf=True` (PDF A5 vertical legible en móvil).
|
- `--pdf` → `emit_pdf=True` (PDF A5 legacy de `render_eda_pdf`, legible en móvil).
|
||||||
|
- `--legacy-only` → emite SOLO el PDF legacy (sin AutomaticEDA), para casos en que solo se quiera el PDF rápido.
|
||||||
|
- `--lite` / `--bajo-consumo` → `render_automatic_eda(profile_level="lite")`: EDA barato y rápido (CI, vistazo previo, máquina sin GPU/red). Apaga LLM y serie temporal y limita los modelos a **PCA + normalidad** (sin KMeans ni IsolationForest, lo caro en CPU), con `sample` reducido. `--full` → `profile_level="full"` (standard + narrativa LLM). Por defecto `profile_level="standard"` (comportamiento histórico). Un flag explícito (`--llm`, `--models`, ...) prima sobre el preset.
|
||||||
|
|
||||||
Por defecto, para un EDA "completo" cuando el usuario no especifica, activa `run_models`, `run_series` y `emit_pdf`; deja `run_llm` para cuando lo pida o cuando interese la interpretación semántica (es la única parte que gasta tokens del modelo).
|
Por defecto, **un EDA completo emite SIEMPRE el informe AutomaticEDA en sus dos formatos: PDF (A5 móvil) Y PPTX (16:9 para compartir)** con los 11 capítulos poblados (portada, overview, distribuciones, calidad, correlaciones, modelos, series, geoespacial, agregación, interpretación LLM). Usa el pipeline `render_automatic_eda` (o `profile_table(emit_automatic=True)`), que activa `run_models` y `run_series` para que los capítulos de modelos/series/geoespacial/agregación salgan poblados. Deja `run_llm` para cuando el usuario lo pida o interese la interpretación semántica + narrativa por capítulo (es la única parte que gasta tokens del modelo).
|
||||||
|
|
||||||
## Reglas duras
|
## Reglas duras
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ Por defecto, para un EDA "completo" cuando el usuario no especifica, activa `run
|
|||||||
2. **CSV/Parquet/Excel** entran cargándolos antes a DuckDB (`read_csv_auto`/`read_parquet`/`read_xlsx`) — DuckDB es el motor por defecto. No traigas la tabla entera a RAM.
|
2. **CSV/Parquet/Excel** entran cargándolos antes a DuckDB (`read_csv_auto`/`read_parquet`/`read_xlsx`) — DuckDB es el motor por defecto. No traigas la tabla entera a RAM.
|
||||||
3. **Secretos**: si la fuente es un DSN PostgreSQL con credenciales, NO las imprimas en los reports ni en el notebook; resuélvelas vía `resolve_pg_dsn`/`pass` cuando aplique.
|
3. **Secretos**: si la fuente es un DSN PostgreSQL con credenciales, NO las imprimas en los reports ni en el notebook; resuélvelas vía `resolve_pg_dsn`/`pass` cuando aplique.
|
||||||
4. **El report es un artefacto local**: vive en `reports/` (gitignored), no se sube a Gitea ni se versiona. Compartir = pasar la ruta (regla `reports.md`).
|
4. **El report es un artefacto local**: vive en `reports/` (gitignored), no se sube a Gitea ni se versiona. Compartir = pasar la ruta (regla `reports.md`).
|
||||||
5. **Entrega las 4 salidas**: JSON sidecar + Markdown + **PDF móvil** + **notebook Jupyter colaborativo ejecutado en vivo**.
|
5. **Entrega las salidas**: el informe **AutomaticEDA PDF + PPTX** (siempre, con `render_automatic_eda` / `emit_automatic=True`) + (opcional) JSON sidecar + Markdown + PDF legacy + **notebook Jupyter colaborativo ejecutado en vivo**. Comparte las rutas de PDF y PPTX.
|
||||||
|
|
||||||
## Paso 1 — Perfilar y escribir los reports
|
## Paso 1 — Perfilar y escribir los reports
|
||||||
|
|
||||||
@@ -43,18 +45,27 @@ Una tabla (caso normal):
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
PYTHONPATH=python/functions python/.venv/bin/python3 - <<'PYEOF'
|
PYTHONPATH=python/functions python/.venv/bin/python3 - <<'PYEOF'
|
||||||
from pipelines.profile_table import profile_table
|
from pipelines.render_automatic_eda import render_automatic_eda
|
||||||
r = profile_table(
|
# Informe AutomaticEDA COMPLETO one-shot: perfil + ctx (datos crudos) + PDF + PPTX
|
||||||
|
# con los 11 capítulos poblados (clusters pintados, evolución temporal, mapa,
|
||||||
|
# tablas de agregación). run_llm=True añade la narrativa LLM por capítulo.
|
||||||
|
r = render_automatic_eda(
|
||||||
"/ruta/datos.duckdb", "ventas",
|
"/ruta/datos.duckdb", "ventas",
|
||||||
run_models=True, run_series=True, emit_pdf=True, run_llm=False,
|
profile_level="standard", # "lite" = bajo consumo CPU/LLM; "full" = + narrativa LLM
|
||||||
|
out_dir="reports",
|
||||||
)
|
)
|
||||||
print("status:", r["status"])
|
print("status:", r["status"])
|
||||||
print("md: ", r["report_md_path"])
|
print("pdf: ", r["pdf_path"], "(", r["n_pages"], "págs )")
|
||||||
print("json: ", r["report_json_path"])
|
print("pptx: ", r["pptx_path"], "(", r["n_slides"], "slides )")
|
||||||
print("pdf: ", r["pdf_path"])
|
print("manifest:", r["manifest_path"])
|
||||||
PYEOF
|
PYEOF
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Si además quieres el report Markdown + JSON sidecar y/o el PDF legacy junto al
|
||||||
|
AutomaticEDA, usa `profile_table(emit_automatic=True, emit_pdf=True, write_report=True)`:
|
||||||
|
emite todo a la vez (`report_md_path`, `report_json_path`, `pdf_path` legacy,
|
||||||
|
`aeda_pdf_path`, `aeda_pptx_path`, `aeda_manifest_path`).
|
||||||
|
|
||||||
Una base entera (todas las tablas + relaciones FK):
|
Una base entera (todas las tablas + relaciones FK):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -90,6 +101,7 @@ Sigue la memoria `eda-workflow-registry` y la regla `notebook_collaboration.md`:
|
|||||||
## Notas
|
## Notas
|
||||||
|
|
||||||
- El `TableProfile` lleva ahora, además del perfilado base y las correlaciones con FDR: `series` (por columna numérica, con `run_series`), `reexpression` por columna numérica (escalera de Tukey) y `caveats` (siempre, avisos exploratorios). El Markdown y el PDF renderizan estas secciones automáticamente cuando están presentes.
|
- El `TableProfile` lleva ahora, además del perfilado base y las correlaciones con FDR: `series` (por columna numérica, con `run_series`), `reexpression` por columna numérica (escalera de Tukey) y `caveats` (siempre, avisos exploratorios). El Markdown y el PDF renderizan estas secciones automáticamente cuando están presentes.
|
||||||
- El PDF (`emit_pdf`) está pensado para leerse en el móvil (A5 vertical, tipografía grande, gráficos Tufte). Se escribe junto al Markdown en `reports/`.
|
- El informe **AutomaticEDA** (`render_automatic_eda` / `emit_automatic=True`) emite el MISMO documento por capítulos a **PDF (A5 móvil)** y **PPTX (16:9)** con garantía de no-corte (texto envuelto, tablas partidas repitiendo cabecera, figuras escaladas) y negrita real (`**texto**`). Escribe `automatic_eda_manifest.json` con la versión de cada capítulo. Los capítulos modelos/series/geoespacial/agregación se pueblan con los datos crudos que `build_eda_render_ctx` muestrea de la base (no se traen tablas enteras a RAM).
|
||||||
|
- El PDF legacy (`emit_pdf`, `render_eda_pdf`) sigue disponible y es independiente del AutomaticEDA (A5 vertical, gráficos Tufte). Se escribe junto al Markdown en `reports/`.
|
||||||
- `run_series` ordena por la primera columna datetime si existe; si no, por el orden físico de filas. Necesita ≥8 puntos válidos por columna.
|
- `run_series` ordena por la primera columna datetime si existe; si no, por el orden físico de filas. Necesita ≥8 puntos válidos por columna.
|
||||||
- Fuentes: DuckDB (CSV/Parquet/Excel cargados antes) y PostgreSQL (`backend="postgres"`). `profile_database` (multi-tabla + FK) es solo DuckDB por ahora.
|
- Fuentes: DuckDB (CSV/Parquet/Excel cargados antes) y PostgreSQL (`backend="postgres"`). `profile_database` (multi-tabla + FK) es solo DuckDB por ahora.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Muestra la flota de Claudes vivos (sessionId + objetivo + estado) y, con argumento, salta con foco a esa conversación dentro de la sesión tmux fleet.
|
description: Muestra la flota de Claudes vivos (sessionId + objetivo + estado) y, con argumento, salta con foco a esa conversación dentro de la sesión tmux fleet. `/fleet show` trae la TUI al contexto tmux actual.
|
||||||
argument-hint: "[texto|sessionId|PID para saltar — vacío = listar la flota]"
|
argument-hint: "[show | texto|sessionId|PID para saltar — vacío = listar la flota]"
|
||||||
---
|
---
|
||||||
|
|
||||||
# /fleet — ver y navegar la flota de Claudes
|
# /fleet — ver y navegar la flota de Claudes
|
||||||
@@ -33,9 +33,32 @@ cd "${FN_REGISTRY_ROOT:-$HOME/fn_registry}/apps/fleetview" && go build -o fleetv
|
|||||||
- la sesión actual / orquestador si la puedes identificar (su `session_id` coincide con el de quien invoca).
|
- la sesión actual / orquestador si la puedes identificar (su `session_id` coincide con el de quien invoca).
|
||||||
4. Si la lista está vacía, indícalo y sugiere que el perfil fleet podría no estar activo (revisar `$FLEET_SOCKET` y que la sesión tmux exista).
|
4. Si la lista está vacía, indícalo y sugiere que el perfil fleet podría no estar activo (revisar `$FLEET_SOCKET` y que la sesión tmux exista).
|
||||||
|
|
||||||
|
### `show` → traer la TUI al contexto tmux actual
|
||||||
|
|
||||||
|
Si `$ARGUMENTS` es exactamente `show` (alias `open`/`attach`), el usuario quiere
|
||||||
|
volver a ver el panel FleetView en el contexto/pane actual sin abrir ninguna
|
||||||
|
ventana ni arrancar una flota nueva. Ejecuta:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"${FN_REGISTRY_ROOT:-$HOME/fn_registry}/apps/fleetview/fleetview" show
|
||||||
|
```
|
||||||
|
|
||||||
|
Comportamiento (decidido por la app, no abre terminal externa):
|
||||||
|
|
||||||
|
- **dentro de tmux con la flota viva** → `select-window` de la window `console`
|
||||||
|
del socket fleet (trae la TUI al frente; no abre nada).
|
||||||
|
- **fuera de tmux** → `attach` a la sesión fleet en la terminal actual (la reutiliza).
|
||||||
|
- **sin flota viva** → error claro, exit 1, no abre nada (sugiere arrancarla con
|
||||||
|
`fleetclaude`).
|
||||||
|
|
||||||
|
Es el equivalente del comportamiento de `fleetclaude` sin args invocado dentro de
|
||||||
|
una flota viva (reuse de contexto): úsalo cuando ya tengas una flota corriendo y
|
||||||
|
solo quieras recuperar la vista del panel. Para abrir una flota NUEVA aparte, usa
|
||||||
|
`fleetclaude --new` (no este comando).
|
||||||
|
|
||||||
### Con argumentos → saltar con foco
|
### Con argumentos → saltar con foco
|
||||||
|
|
||||||
El usuario quiere que la interfaz tmux salte a una conversación concreta. `$ARGUMENTS` es el query: texto del objetivo, prefijo de `sessionId`, o PID.
|
El usuario quiere que la interfaz tmux salte a una conversación concreta. `$ARGUMENTS` es el query: texto del objetivo, prefijo de `sessionId`, o PID (cualquier valor que no sea `show`).
|
||||||
|
|
||||||
1. Ejecuta:
|
1. Ejecuta:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -31,12 +31,13 @@ Diferencia con `dev/flows/`:
|
|||||||
|
|
||||||
**Fase 1 (manual via Claude):**
|
**Fase 1 (manual via Claude):**
|
||||||
|
|
||||||
El agente lee `dev/issues/*.md`, parsea frontmatter YAML con `yaml.safe_load`, aplica el filtro, imprime tabla.
|
El agente lee `dev/issues/**/*.md` (recursivo: incluye subcarpetas por dominio como `dev/issues/kanban/`, `dev/issues/cpp/`, ... excluyendo `completed/`), parsea frontmatter YAML con `yaml.safe_load`, aplica el filtro, imprime tabla.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import yaml, pathlib, re
|
import yaml, pathlib, re
|
||||||
issues = []
|
issues = []
|
||||||
for f in pathlib.Path("dev/issues").glob("*.md"):
|
for f in pathlib.Path("dev/issues").glob("**/*.md"):
|
||||||
|
if f.parent.name == "completed": continue
|
||||||
if f.name in {"README.md", "template.md"}: continue
|
if f.name in {"README.md", "template.md"}: continue
|
||||||
txt = f.read_text()
|
txt = f.read_text()
|
||||||
m = re.match(r"^---\n(.*?)\n---", txt, re.S)
|
m = re.match(r"^---\n(.*?)\n---", txt, re.S)
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
"enabledMcpjsonServers": [
|
"enabledMcpjsonServers": [
|
||||||
"registry",
|
"registry",
|
||||||
"jupyter",
|
"jupyter",
|
||||||
"orchestrator"
|
"orchestrator",
|
||||||
|
"godot",
|
||||||
|
"ardour"
|
||||||
],
|
],
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"PreToolUse": [
|
"PreToolUse": [
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ reports/*
|
|||||||
!reports/.gitkeep
|
!reports/.gitkeep
|
||||||
projects/*/reports/
|
projects/*/reports/
|
||||||
|
|
||||||
|
# Papers — artefacto local: papers académicos reproducibles. En fase interna viven
|
||||||
|
# local y gitignored (como los reports); al promocionar a fase publishable se
|
||||||
|
# vuelven sub-repo Gitea propio (como apps/analyses). Solo el marcador .gitkeep se
|
||||||
|
# versiona. Convención: docs/capabilities/papers.md
|
||||||
|
papers/*
|
||||||
|
!papers/.gitkeep
|
||||||
|
|
||||||
# Node / pnpm
|
# Node / pnpm
|
||||||
**/node_modules/
|
**/node_modules/
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ name: launch_fleetclaude
|
|||||||
kind: function
|
kind: function
|
||||||
lang: bash
|
lang: bash
|
||||||
domain: infra
|
domain: infra
|
||||||
version: "1.6.0"
|
version: "1.7.0"
|
||||||
purity: impure
|
purity: impure
|
||||||
signature: "launch_fleetclaude [--cwd <dir>] [--bin <path>] [--session <name>] [--reuse] [--cols <n>]"
|
signature: "launch_fleetclaude [--cwd <dir>] [--bin <path>] [--session <name>] [--reuse] [--new] [--cols <n>]"
|
||||||
description: "Entrypoint de FleetView: abre una ventana de terminal con una sesion tmux (socket aislado por perfil) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. La terminal se AUTO-DETECTA sin config por PC: kitty si esta instalado y hay display ($DISPLAY/$WAYLAND_DISPLAY), si no Windows Terminal (wt.exe) en WSL adjuntando via wsl.exe. El pane de la TUI corre dentro del bucle supervisor supervise_fleetview_tui, que la relanza si muere (crash/panic/kill), asi el panel de control NUNCA se pierde. Soporta PERFILES multiples: sin --session/--reuse cada invocacion abre un perfil nuevo (fleet, fleet2, fleet3, ...) con su propia flota; inyecta FLEET_SOCKET/FLEET_SESSION a la TUI para que cada panel vea solo sus Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
|
description: "Entrypoint de FleetView: abre una ventana de terminal con una sesion tmux (socket aislado por perfil) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. REUSO DE CONTEXTO: si se invoca DENTRO de una flota tmux viva (su window 'console') sin --new, NO abre ventana ni crea un perfil nuevo; trae la TUI al pane/contexto actual (equivale a 'fleetview show'). El flag --new fuerza una flota+ventana nueva aunque estes en tmux. La terminal se AUTO-DETECTA sin config por PC: kitty si esta instalado y hay display ($DISPLAY/$WAYLAND_DISPLAY), si no Windows Terminal (wt.exe) en WSL adjuntando via wsl.exe. El pane de la TUI corre dentro del bucle supervisor supervise_fleetview_tui, que la relanza si muere (crash/panic/kill), asi el panel de control NUNCA se pierde. Soporta PERFILES multiples: fuera de tmux, o con --new, cada invocacion abre un perfil nuevo (fleet, fleet2, fleet3, ...) con su propia flota; inyecta FLEET_SOCKET/FLEET_SESSION a la TUI para que cada panel vea solo sus Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
|
||||||
tags: [claude-fleet, infra, kitty, tmux, claude, fleetview, launcher, wsl, windows-terminal]
|
tags: [claude-fleet, infra, kitty, tmux, claude, fleetview, launcher, wsl, windows-terminal]
|
||||||
params:
|
params:
|
||||||
- name: --cwd
|
- name: --cwd
|
||||||
@@ -14,12 +14,14 @@ params:
|
|||||||
- name: --bin
|
- name: --bin
|
||||||
desc: "Ruta al binario de la TUI fleetview que corre en el pane izquierdo. Opcional. Default: <repo>/apps/fleetview/fleetview. Si no es ejecutable, el pane izquierdo muestra un mensaje de como compilarla y deja una shell viva."
|
desc: "Ruta al binario de la TUI fleetview que corre en el pane izquierdo. Opcional. Default: <repo>/apps/fleetview/fleetview. Si no es ejecutable, el pane izquierdo muestra un mensaje de como compilarla y deja una shell viva."
|
||||||
- name: --session
|
- name: --session
|
||||||
desc: "Fija el perfil (socket+sesion tmux comparten nombre) por nombre exacto; reutiliza el existente si ya vive (idempotente sobre ese nombre). Opcional. Sin esta opcion, el perfil se elige automaticamente (primer nombre libre de la secuencia fleet, fleet2, ...)."
|
desc: "Fija el perfil (socket+sesion tmux comparten nombre) por nombre exacto; reutiliza el existente si ya vive (idempotente sobre ese nombre). Opcional. Sin esta opcion, el perfil se elige automaticamente (primer nombre libre de la secuencia fleet, fleet2, ...). Invocado DENTRO de tmux con un nombre DISTINTO al de la flota actual equivale a --new (pides otra flota: ventana nueva, sin reuse de contexto)."
|
||||||
- name: --reuse
|
- name: --reuse
|
||||||
desc: "Reattach al perfil principal 'fleet' en vez de abrir uno nuevo. Opcional. Recupera el comportamiento idempotente clasico (volver a invocar NO duplica la flota, reusa la existente)."
|
desc: "Reattach al perfil principal 'fleet' en vez de abrir uno nuevo. Opcional. Recupera el comportamiento idempotente clasico (volver a invocar NO duplica la flota, reusa la existente)."
|
||||||
|
- name: --new
|
||||||
|
desc: "Fuerza una flota NUEVA en una ventana NUEVA (kitty/wt.exe) incluso estando dentro de una flota tmux. Opcional. Es la via explicita para abrir una FleetView aparte; sin este flag, invocado dentro de una flota viva se reusa el contexto actual (no abre ventana ni crea perfil)."
|
||||||
- name: --cols
|
- name: --cols
|
||||||
desc: "Ancho en columnas del pane izquierdo (la TUI). Opcional. Default: 40."
|
desc: "Ancho en columnas del pane izquierdo (la TUI). Opcional. Default: 40."
|
||||||
output: "Crea/reutiliza una sesion tmux detached con dos panes y lanza una ventana de terminal 'FleetView' adjunta a ella (kitty o Windows Terminal segun auto-deteccion), desacoplada del shell padre. Imprime el estado por stdout. Sin valor de retorno; exit 0 en exito."
|
output: "Caso reuse de contexto (dentro de una flota tmux viva, sin --new): trae la TUI al pane/contexto actual con select-window de la window 'console' (o 'fleetview show' si el binario existe) y retorna 0, sin abrir nada. Caso ventana-nueva (fuera de tmux, o con --new): crea/reutiliza una sesion tmux detached con dos panes y lanza una ventana de terminal 'FleetView' adjunta (kitty o Windows Terminal segun auto-deteccion), desacoplada del shell padre. Imprime el estado por stdout. Sin valor de retorno; exit 0 en exito, !=0 con mensaje claro si no hay terminal ni contexto que reusar."
|
||||||
uses_functions:
|
uses_functions:
|
||||||
- supervise_fleetview_tui_bash_infra
|
- supervise_fleetview_tui_bash_infra
|
||||||
uses_types: []
|
uses_types: []
|
||||||
@@ -36,32 +38,44 @@ file_path: "bash/functions/infra/launch_fleetclaude.sh"
|
|||||||
## Ejemplo
|
## Ejemplo
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Via fn run (resuelve por nombre o ID):
|
# DENTRO de una flota tmux viva (p. ej. en el pane del orquestador): reusa el
|
||||||
fn run launch_fleetclaude
|
# contexto, trae la TUI al pane actual. NO abre ventana ni crea perfil nuevo.
|
||||||
|
fleetclaude
|
||||||
|
|
||||||
# Perfil nuevo automatico (fleet la 1a vez; fleet2, fleet3, ... si ya hay uno):
|
# FUERA de tmux: perfil nuevo automatico (fleet la 1a vez; fleet2, ... si ya hay
|
||||||
launch_fleetclaude
|
# uno) en una ventana de terminal nueva, reutilizando la terminal actual (attach):
|
||||||
|
fleetclaude
|
||||||
|
|
||||||
|
# Forzar una flota+ventana NUEVA aunque estes dentro de una flota tmux:
|
||||||
|
fleetclaude --new
|
||||||
|
|
||||||
# Reattach a la flota principal 'fleet' (comportamiento idempotente clasico):
|
# Reattach a la flota principal 'fleet' (comportamiento idempotente clasico):
|
||||||
launch_fleetclaude --reuse
|
fleetclaude --reuse
|
||||||
|
|
||||||
# Perfil con nombre fijo y ancho de pane personalizado:
|
# Perfil con nombre fijo y ancho de pane personalizado:
|
||||||
launch_fleetclaude --session trabajo --cols 50
|
fleetclaude --session trabajo --cols 50
|
||||||
|
|
||||||
|
# Via fn run (resuelve por nombre o ID):
|
||||||
|
fn run launch_fleetclaude
|
||||||
```
|
```
|
||||||
|
|
||||||
Tras invocarlo aparece una ventana de terminal titulada `FleetView (<perfil>)` con dos
|
Dentro de una flota viva, `fleetclaude` sin args reusa el contexto (la window
|
||||||
panes lado a lado: a la izquierda la TUI `fleetview`, a la derecha una sesion de
|
`console` pasa al frente). Fuera de tmux (o con `--new`) aparece una ventana de
|
||||||
`claude --dangerously-skip-permissions`. Cada perfil es un socket+sesion tmux
|
terminal titulada `FleetView (<perfil>)` con dos panes lado a lado: a la izquierda
|
||||||
aislados con su propia flota: puedes tener varias FleetView abiertas a la vez.
|
la TUI `fleetview`, a la derecha una sesion de `claude --dangerously-skip-permissions`.
|
||||||
Por defecto, volver a invocarlo abre un perfil NUEVO (no reusa); usa `--reuse`
|
Cada perfil es un socket+sesion tmux aislados con su propia flota: puedes tener
|
||||||
o `--session <nombre>` para volver a una flota concreta.
|
varias FleetView abiertas a la vez con `--new`.
|
||||||
|
|
||||||
## Cuando usarla
|
## Cuando usarla
|
||||||
|
|
||||||
Usala cuando quieras un unico punto de entrada a la flota de Claudes en vez de
|
Usala cuando quieras un unico punto de entrada a la flota de Claudes en vez de
|
||||||
N ventanas kitty sueltas: lanzas `fleetclaude` y tienes la TUI de control y un
|
N ventanas kitty sueltas: lanzas `fleetclaude` y tienes la TUI de control y un
|
||||||
Claude listo para trabajar en la misma ventana. Tipico al empezar la jornada o
|
Claude listo para trabajar en la misma ventana. Tipico al empezar la jornada o
|
||||||
al retomar el trabajo en el repo `fn_registry`.
|
al retomar el trabajo en el repo `fn_registry`. Si **ya estas dentro de una
|
||||||
|
flota** (en el pane del orquestador) y solo quieres volver a ver la TUI, lanza
|
||||||
|
`fleetclaude` sin args: trae el panel al contexto actual sin abrir otra ventana
|
||||||
|
ni arrancar una flota duplicada. Usa `--new` solo cuando quieras DELIBERADAMENTE
|
||||||
|
una segunda flota aparte.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
@@ -87,10 +101,27 @@ al retomar el trabajo en el repo `fn_registry`.
|
|||||||
funciona en un PC con kitty y en otro WSL sin kitty, cada uno elige su
|
funciona en un PC con kitty y en otro WSL sin kitty, cada uno elige su
|
||||||
terminal. Causa raiz del sintoma "se lanza la flota pero no se ve": kitty no
|
terminal. Causa raiz del sintoma "se lanza la flota pero no se ve": kitty no
|
||||||
instalado en WSL hacia que la sesion tmux se creara sin ventana que la mostrara.
|
instalado en WSL hacia que la sesion tmux se creara sin ventana que la mostrara.
|
||||||
- **Dentro de tmux abre ventana nueva**: si invocas `fleetclaude` desde dentro de
|
- **Dentro de una flota tmux viva: reuse de contexto (no ventana nueva)**: si
|
||||||
una sesion tmux (`$TMUX` definido), NO hace `attach` anidado (rompe / avisa de
|
invocas `fleetclaude` sin `--new` desde dentro de una flota fleetview viva
|
||||||
nesting); cae a la ruta ventana-nueva (auto-deteccion de terminal). Fuera de
|
(`$TMUX` definido y el socket actual tiene una sesion homonima con window
|
||||||
tmux y con TTY, reutiliza la terminal actual con `exec tmux attach`.
|
`console`), NO abre ventana ni crea un perfil `fleetN+1`: trae la TUI al pane
|
||||||
|
actual (`fleetview show`, o `tmux -L <perfil> select-window -t <perfil>:console`
|
||||||
|
si el binario no esta compilado) y retorna 0. El perfil de la flota actual se
|
||||||
|
deriva de `$TMUX` (basename del socket = nombre `-L`), senal fiable aunque
|
||||||
|
`$FLEET_SOCKET` venga vacio (ver `detect_fleet_context`). **`--new`** fuerza el
|
||||||
|
comportamiento clasico (flota+ventana nueva); pasar `--session <otro>` distinto
|
||||||
|
al perfil actual equivale a `--new` implicito. Fuera de tmux y con TTY, reutiliza
|
||||||
|
la terminal actual con `exec tmux attach` (nunca `attach` anidado dentro de
|
||||||
|
tmux). Sin TTY ni contexto que reusar (atajo de escritorio/cron) cae a la ruta
|
||||||
|
ventana-nueva. Antes de este fix (v1.6.0 y anteriores) cualquier `fleetclaude`
|
||||||
|
dentro de tmux abria una kitty nueva y un socket `fleetN+1` — el sintoma que
|
||||||
|
acumulaba 6+ sockets `fleet*`.
|
||||||
|
- **`local x` unbound bajo `set -u`**: el archivo corre con `set -euo pipefail`.
|
||||||
|
`local left_pane right_pane` dejaba esas vars *unbound* (no vacias), asi que la
|
||||||
|
rama "reutilizar sesion existente" (`--reuse`/`--session <vivo>`) reventaba con
|
||||||
|
`left_pane: unbound variable` al evaluar `[[ -z "$left_pane" ]]`. Se inicializan
|
||||||
|
explicitamente a `""` (`local left_pane="" right_pane=""`). Si tocas estas vars,
|
||||||
|
no vuelvas a declararlas sin valor.
|
||||||
- **kitty detached (setsid)**: la ventana kitty se lanza con `setsid ... &` para
|
- **kitty detached (setsid)**: la ventana kitty se lanza con `setsid ... &` para
|
||||||
sobrevivir al cierre de la terminal que la invoco. La ventana de Windows
|
sobrevivir al cierre de la terminal que la invoco. La ventana de Windows
|
||||||
Terminal (wt.exe) ya es un proceso Windows independiente del arbol Linux, asi
|
Terminal (wt.exe) ya es un proceso Windows independiente del arbol Linux, asi
|
||||||
@@ -128,15 +159,29 @@ al retomar el trabajo en el repo `fn_registry`.
|
|||||||
- **Ancho del sidebar via hooks**: `client-resized` y `window-layout-changed`
|
- **Ancho del sidebar via hooks**: `client-resized` y `window-layout-changed`
|
||||||
re-fijan el pane 0 (TUI) a `--cols` columnas, porque el `attach` de kitty y el
|
re-fijan el pane 0 (TUI) a `--cols` columnas, porque el `attach` de kitty y el
|
||||||
conmutar de Claude redistribuyen el espacio.
|
conmutar de Claude redistribuyen el espacio.
|
||||||
- **tmux siempre; terminal (kitty/wt.exe) solo sin TTY**: `tmux` es obligatorio
|
- **tmux siempre; terminal (kitty/wt.exe) solo en la ruta ventana-nueva**: `tmux`
|
||||||
(aborta != 0 si falta). Una terminal nueva (kitty o Windows Terminal) solo se
|
es obligatorio (aborta != 0 si falta). Una terminal nueva (kitty o Windows
|
||||||
necesita en la ruta sin-TTY (dentro de tmux, atajo de escritorio, cron, script),
|
Terminal) solo se necesita en la ruta ventana-nueva: `--new`, o sin TTY ni flota
|
||||||
donde abre una ventana nueva. Invocado desde una terminal interactiva fuera de
|
viva que reusar (atajo de escritorio, cron, script). Dentro de una flota viva sin
|
||||||
tmux (el caso normal del alias `fleetclaude`), reutiliza la terminal actual con
|
`--new` se reusa el contexto (ni kitty ni wt.exe). Invocado desde una terminal
|
||||||
`exec tmux attach` y no necesita ni kitty ni wt.exe.
|
interactiva fuera de tmux (el caso normal del alias `fleetclaude`), reutiliza la
|
||||||
|
terminal actual con `exec tmux attach` y tampoco necesita kitty ni wt.exe.
|
||||||
|
|
||||||
## Capability growth log
|
## Capability growth log
|
||||||
|
|
||||||
|
- v1.7.0 (2026-06-30) — **reuse de contexto dentro de la flota + flag `--new`**.
|
||||||
|
Invocado sin `--new` desde dentro de una flota tmux viva (su window `console`),
|
||||||
|
`fleetclaude` ya NO abre una kitty nueva ni crea un perfil `fleetN+1`: trae la
|
||||||
|
TUI al pane/contexto actual (`fleetview show`, o `tmux -L <perfil> select-window
|
||||||
|
-t <perfil>:console` como fallback sin binario) y retorna 0. El perfil actual se
|
||||||
|
deriva de `$TMUX` (basename del socket); pasar `--session <otro>` distinto al
|
||||||
|
actual equivale a `--new` implicito. Nuevo flag `--new` para forzar la ruta
|
||||||
|
clasica (flota+ventana nueva) aun dentro de tmux. Fuera de tmux el comportamiento
|
||||||
|
es intacto (`exec tmux attach` reutiliza la terminal). Arregla el sintoma de que
|
||||||
|
lanzar `fleetclaude` dentro de una flota abria ventana kitty + socket nuevo
|
||||||
|
(`fleet7`, `fleet8`, ...). Fix incidental: `local left_pane="" right_pane=""`
|
||||||
|
(antes `local left_pane right_pane` reventaba con `unbound variable` bajo
|
||||||
|
`set -u` al reutilizar una sesion existente).
|
||||||
- v1.6.0 (2026-06-29) — **auto-deteccion de terminal (kitty ↔ Windows Terminal)**.
|
- v1.6.0 (2026-06-29) — **auto-deteccion de terminal (kitty ↔ Windows Terminal)**.
|
||||||
La ruta ventana-nueva ya no asume kitty: elige terminal segun el host. kitty si
|
La ruta ventana-nueva ya no asume kitty: elige terminal segun el host. kitty si
|
||||||
esta instalado y hay display (`$DISPLAY`/`$WAYLAND_DISPLAY`); si no, en WSL abre
|
esta instalado y hay display (`$DISPLAY`/`$WAYLAND_DISPLAY`); si no, en WSL abre
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ launch_fleetclaude() {
|
|||||||
local cols=52
|
local cols=52
|
||||||
local explicit_session=0 # 1 si el usuario pasó --session <name> a mano
|
local explicit_session=0 # 1 si el usuario pasó --session <name> a mano
|
||||||
local reuse=0 # 1 si el usuario pidió --reuse (reattach al perfil principal)
|
local reuse=0 # 1 si el usuario pidió --reuse (reattach al perfil principal)
|
||||||
|
local want_new=0 # 1 si el usuario pidió --new (forzar flota+ventana nueva)
|
||||||
local T="" # socket tmux aislado; se fija al resolver el perfil
|
local T="" # socket tmux aislado; se fija al resolver el perfil
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
@@ -46,6 +47,9 @@ launch_fleetclaude() {
|
|||||||
--reuse)
|
--reuse)
|
||||||
reuse=1
|
reuse=1
|
||||||
;;
|
;;
|
||||||
|
--new)
|
||||||
|
want_new=1
|
||||||
|
;;
|
||||||
--cols)
|
--cols)
|
||||||
shift
|
shift
|
||||||
cols="${1:-40}"
|
cols="${1:-40}"
|
||||||
@@ -62,6 +66,11 @@ Claudes). Sin --session ni --reuse, cada invocacion abre un perfil NUEVO: usa
|
|||||||
el primer nombre libre de la secuencia fleet, fleet2, fleet3, ... Asi puedes
|
el primer nombre libre de la secuencia fleet, fleet2, fleet3, ... Asi puedes
|
||||||
tener varias FleetView abiertas a la vez, cada una con su flota independiente.
|
tener varias FleetView abiertas a la vez, cada una con su flota independiente.
|
||||||
|
|
||||||
|
REUSO DE CONTEXTO: si ya estas DENTRO de una flota tmux viva (p. ej. en el pane
|
||||||
|
del orquestador), 'fleetclaude' sin args NO abre una ventana ni crea un perfil
|
||||||
|
nuevo: trae la TUI al contexto/pane actual (equivale a 'fleetview show'). Para
|
||||||
|
abrir explicitamente una flota aparte en una ventana nueva, usa --new.
|
||||||
|
|
||||||
Opciones:
|
Opciones:
|
||||||
--cwd <dir> Directorio de trabajo de los panes.
|
--cwd <dir> Directorio de trabajo de los panes.
|
||||||
Default: raiz del repo fn_registry (derivada dinamicamente).
|
Default: raiz del repo fn_registry (derivada dinamicamente).
|
||||||
@@ -69,13 +78,21 @@ Opciones:
|
|||||||
Default: <repo>/apps/fleetview/fleetview
|
Default: <repo>/apps/fleetview/fleetview
|
||||||
--session <name> Fija el perfil (socket+sesion) por nombre exacto; reutiliza
|
--session <name> Fija el perfil (socket+sesion) por nombre exacto; reutiliza
|
||||||
el existente si ya esta vivo. Sin esta opcion, perfil auto.
|
el existente si ya esta vivo. Sin esta opcion, perfil auto.
|
||||||
|
Si se invoca DENTRO de tmux con un nombre DISTINTO al de la
|
||||||
|
flota actual, equivale a --new (pides otra flota).
|
||||||
--reuse Reattach al perfil principal 'fleet' en vez de abrir uno
|
--reuse Reattach al perfil principal 'fleet' en vez de abrir uno
|
||||||
nuevo (vuelve al comportamiento idempotente clasico).
|
nuevo (vuelve al comportamiento idempotente clasico).
|
||||||
|
--new Fuerza una flota NUEVA en una ventana NUEVA (kitty/wt.exe),
|
||||||
|
incluso dentro de tmux. Es la via explicita para tener una
|
||||||
|
FleetView aparte; sin este flag, dentro de tmux se reusa el
|
||||||
|
contexto actual.
|
||||||
--cols <n> Ancho (columnas) del pane izquierdo. Default: 40.
|
--cols <n> Ancho (columnas) del pane izquierdo. Default: 40.
|
||||||
-h, --help Muestra esta ayuda.
|
-h, --help Muestra esta ayuda.
|
||||||
|
|
||||||
Ejemplos:
|
Ejemplos:
|
||||||
launch_fleetclaude # perfil nuevo (fleet, luego fleet2, ...)
|
launch_fleetclaude # dentro de la flota: reusa el contexto;
|
||||||
|
# fuera de tmux: perfil nuevo (fleet, ...)
|
||||||
|
launch_fleetclaude --new # flota+ventana nueva aunque estes en tmux
|
||||||
launch_fleetclaude --reuse # reattach a la flota principal 'fleet'
|
launch_fleetclaude --reuse # reattach a la flota principal 'fleet'
|
||||||
launch_fleetclaude --session trabajo # perfil con nombre fijo 'trabajo'
|
launch_fleetclaude --session trabajo # perfil con nombre fijo 'trabajo'
|
||||||
launch_fleetclaude --cwd ~/fn_registry --cols 50
|
launch_fleetclaude --cwd ~/fn_registry --cols 50
|
||||||
@@ -127,6 +144,45 @@ USAGE
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# REUSO DE CONTEXTO (sin --new): si ya estamos DENTRO de una flota tmux
|
||||||
|
# viva, 'fleetclaude' sin args NO abre una ventana/terminal nueva ni crea
|
||||||
|
# un perfil fleetN+1 — trae la TUI al contexto/pane actual, igual que
|
||||||
|
# 'fleetview show'. El flag --new fuerza el comportamiento clasico (flota
|
||||||
|
# nueva en ventana nueva); --reuse mantiene su semantica historica.
|
||||||
|
#
|
||||||
|
# El perfil de la flota actual se deriva de $TMUX (el basename del socket
|
||||||
|
# es el nombre -L; senal fiable aunque $FLEET_SOCKET venga vacio, ver
|
||||||
|
# detect_fleet_context). Si se paso --session con un nombre DISTINTO al
|
||||||
|
# actual, es pedir OTRA flota -> se trata como --new implicito (no reusa).
|
||||||
|
# "Flota viva" = el socket tiene una sesion homonima con una window
|
||||||
|
# 'console' (la firma de una FleetView), no un tmux cualquiera.
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
if [[ "$want_new" -eq 0 && "$reuse" -eq 0 && -n "${TMUX:-}" ]]; then
|
||||||
|
local current_socket target_socket
|
||||||
|
current_socket="$(basename "${TMUX%%,*}")"
|
||||||
|
target_socket="$current_socket"
|
||||||
|
[[ "$explicit_session" -eq 1 ]] && target_socket="$session"
|
||||||
|
|
||||||
|
if [[ "$target_socket" == "$current_socket" ]] \
|
||||||
|
&& tmux -L "$current_socket" has-session -t "$current_socket" 2>/dev/null \
|
||||||
|
&& tmux -L "$current_socket" list-windows -t "$current_socket" \
|
||||||
|
-F '#{window_name}' 2>/dev/null | grep -qx console; then
|
||||||
|
# Traer la TUI al contexto actual sin abrir nada nuevo. Preferimos
|
||||||
|
# el binario (centraliza la politica en la app: 'fleetview show');
|
||||||
|
# si no esta compilado, caemos a 'select-window' directo, que es lo
|
||||||
|
# que 'show' hace por dentro dentro de tmux (cero dependencia).
|
||||||
|
if [[ -x "$bin" ]] \
|
||||||
|
&& FLEET_SOCKET="$current_socket" FLEET_SESSION="$current_socket" \
|
||||||
|
"$bin" show 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
tmux -L "$current_socket" select-window -t "$current_socket":console
|
||||||
|
echo "launch_fleetclaude: flota '$current_socket' viva; TUI traida al contexto actual (sin ventana nueva)."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
# Resolver el PERFIL (socket+sesion tmux comparten nombre).
|
# Resolver el PERFIL (socket+sesion tmux comparten nombre).
|
||||||
#
|
#
|
||||||
@@ -200,7 +256,10 @@ USAGE
|
|||||||
# indice 1 y cualquier referencia a console.0 falla con
|
# indice 1 y cualquier referencia a console.0 falla con
|
||||||
# "can't find pane: 0". Los pane ID son estables e inmunes al base-index.
|
# "can't find pane: 0". Los pane ID son estables e inmunes al base-index.
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
local left_pane right_pane
|
# Inicializadas a "" (no solo declaradas): bajo `set -u` una `local x` sin
|
||||||
|
# valor queda *unbound*, y al reutilizar una sesion existente el `[[ -z
|
||||||
|
# "$left_pane" ]]` de mas abajo reventaba con "unbound variable".
|
||||||
|
local left_pane="" right_pane=""
|
||||||
if $T has-session -t "$session" 2>/dev/null; then
|
if $T has-session -t "$session" 2>/dev/null; then
|
||||||
echo "launch_fleetclaude: la sesion tmux '$session' ya existe; reutilizandola."
|
echo "launch_fleetclaude: la sesion tmux '$session' ya existe; reutilizandola."
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: next_numbered_dir
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: io
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "next_numbered_dir(parent_dir: string, [width: int]) -> string"
|
||||||
|
description: "Calcula el siguiente prefijo numerico NNNN- para un directorio numerado incremental. Escanea los subdirectorios directos de parent_dir cuyo nombre empiece por NNNN- (4+ digitos seguidos de guion), toma el maximo, le suma 1 y lo imprime con zero-padding al ancho width (default 4). Si parent_dir no existe o no tiene subdirs que matcheen, imprime 0001."
|
||||||
|
tags: [papers, io, scaffold]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
params:
|
||||||
|
- name: parent_dir
|
||||||
|
desc: "directorio padre cuyos subdirectorios numerados (NNNN-...) se escanean; obligatorio"
|
||||||
|
- name: width
|
||||||
|
desc: "ancho del zero-padding del numero impreso (default 4); opcional"
|
||||||
|
output: "el siguiente numero como string con zero-padding a width digitos a stdout (ej. 0003); usage a stderr y exit 1 si falta parent_dir"
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/io/next_numbered_dir.sh"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ejemplo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source bash/functions/io/next_numbered_dir.sh
|
||||||
|
|
||||||
|
# Sobre un papers/ que ya contiene 0001-foo y 0002-bar
|
||||||
|
mkdir -p /tmp/papers/{0001-foo,0002-bar}
|
||||||
|
next_numbered_dir /tmp/papers
|
||||||
|
# -> 0003
|
||||||
|
|
||||||
|
# Directorio vacio o inexistente -> primer numero
|
||||||
|
next_numbered_dir /tmp/papers_nuevo
|
||||||
|
# -> 0001
|
||||||
|
|
||||||
|
# Ancho de padding distinto
|
||||||
|
next_numbered_dir /tmp/papers 6
|
||||||
|
# -> 000003
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cuando usarla
|
||||||
|
|
||||||
|
Cuando scaffoldees un artefacto numerado incremental (papers/, reports/, issues/) y necesites el siguiente NNNN sin colision: escanea lo que ya existe en disco y te da el numero libre listo para crear `<NNNN>-<slug>`.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- **Impura**: lee el filesystem (estado del directorio en el momento de la llamada). No crea nada — solo calcula e imprime el numero.
|
||||||
|
- **Octal**: los numeros con cero a la izquierda (`08`, `09`) se interpretan como octal en aritmetica bash y romperian el calculo. La funcion fuerza base 10 con `10#$num` para evitarlo.
|
||||||
|
- **Solo subdirectorios**: cuenta unicamente subdirs directos. Archivos sueltos (`.gitkeep`, `notas.md`) y subdirs que no matcheen el patron se ignoran. No es recursivo.
|
||||||
|
- **Patron estricto**: el prefijo debe ser `NNNN-` (minimo 4 digitos seguidos de guion). Un subdir `12-foo` o `0001foo` (sin guion) NO se cuenta.
|
||||||
|
- No hay deteccion de huecos: devuelve `max+1`, no el primer numero libre intermedio. Si tienes `0001` y `0003`, devuelve `0004`, no `0002`.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# next_numbered_dir — Compute the next NNNN- prefix for a numbered directory.
|
||||||
|
#
|
||||||
|
# Scans the DIRECT subdirectories of <parent_dir> whose names start with a
|
||||||
|
# numeric prefix of the form `NNNN-` (4+ digits followed by a hyphen), takes
|
||||||
|
# the maximum number, adds 1, and prints it zero-padded to <width> (default 4).
|
||||||
|
# If <parent_dir> does not exist or contains no matching subdir, prints the
|
||||||
|
# first number (0001 at default width).
|
||||||
|
|
||||||
|
next_numbered_dir() {
|
||||||
|
local parent_dir="${1:-}"
|
||||||
|
local width="${2:-4}"
|
||||||
|
|
||||||
|
if [[ -z "$parent_dir" ]]; then
|
||||||
|
echo "usage: next_numbered_dir <parent_dir> [width]" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local max=0
|
||||||
|
local entry base num
|
||||||
|
|
||||||
|
if [[ -d "$parent_dir" ]]; then
|
||||||
|
# Iterate only over direct subdirectories. The trailing slash in the
|
||||||
|
# glob ensures files (e.g. .gitkeep) are skipped — only dirs match.
|
||||||
|
for entry in "$parent_dir"/*/; do
|
||||||
|
# If the glob matched nothing it stays literal; guard with -d.
|
||||||
|
[[ -d "$entry" ]] || continue
|
||||||
|
base="$(basename "$entry")"
|
||||||
|
# Require a prefix of 4+ digits followed by a hyphen.
|
||||||
|
if [[ "$base" =~ ^([0-9]{4,})- ]]; then
|
||||||
|
num="${BASH_REMATCH[1]}"
|
||||||
|
# Force base 10 so leading zeros (08, 09) are not read as octal.
|
||||||
|
num=$((10#$num))
|
||||||
|
if (( num > max )); then
|
||||||
|
max=$num
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%0*d\n" "$width" $(( max + 1 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
next_numbered_dir "$@"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
name: init_paper
|
||||||
|
kind: pipeline
|
||||||
|
lang: bash
|
||||||
|
domain: pipelines
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "init_paper(slug: string, [--title <t>] [--domain <d>] [--tags <csv>]) -> void"
|
||||||
|
description: "Scaffold de un paper académico reproducible en papers/<NNNN-slug>/. Calcula el siguiente número incremental escaneando papers/, crea las subcarpetas (experiments data figures reviews out), copia las plantillas paper.md (IMRaD) + preregistration.md (anti-HARKing) rellenando el frontmatter (title, slug, date de hoy, phase=question, status=draft) y crea references.md. NO hace git init: el paper arranca en fase interna local (papers/ gitignored). Grupo de capacidad papers."
|
||||||
|
tags: [papers, scaffold, paper, pipeline, bash, launcher]
|
||||||
|
uses_functions:
|
||||||
|
- next_numbered_dir_bash_io
|
||||||
|
- slugify_ascii_py_core
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
params:
|
||||||
|
- name: slug
|
||||||
|
desc: "identificador legible del paper; se slugifica a ASCII (espacios/acentos se normalizan) y se prefija con el siguiente NNNN incremental"
|
||||||
|
- name: "--title"
|
||||||
|
desc: "título del paper (string); si se omite, usa el slug limpio. No debe contener el carácter '|'"
|
||||||
|
- name: "--domain"
|
||||||
|
desc: "dominio del paper escrito en el frontmatter (default datascience)"
|
||||||
|
- name: "--tags"
|
||||||
|
desc: "tags CSV que se escriben en el frontmatter de paper.md (opcional)"
|
||||||
|
output: "sin salida directa; crea papers/<NNNN-slug>/ con paper.md, preregistration.md, references.md y las subcarpetas experiments/ data/ figures/ reviews/ out/. Imprime el resumen y los pasos siguientes a stdout."
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/pipelines/init_paper.sh"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ejemplo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Scaffold de un paper nuevo (numera 0001, 0002, ... automáticamente)
|
||||||
|
fn run init_paper mi-primer-paper --title "Mi primer paper"
|
||||||
|
fn run init_paper reactive-loop-calls --domain datascience --tags registry,telemetria
|
||||||
|
|
||||||
|
# El slug se slugifica: "Áreas de Mejora" -> papers/0003-areas-de-mejora/
|
||||||
|
fn run init_paper "Áreas de Mejora"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cuando usarla
|
||||||
|
|
||||||
|
Cuando empiezas un paper académico nuevo dentro de `fn_registry` y necesitas el esqueleto del artefacto (`papers/<NNNN-slug>/`) con las plantillas IMRaD y de pre-registro listas para rellenar. Es el paso 1 del grupo de capacidad `papers` (ver `docs/capabilities/papers.md`), antes de la revisión de literatura y del pre-registro de la hipótesis.
|
||||||
|
|
||||||
|
## Flujo
|
||||||
|
|
||||||
|
1. Parsea `<slug>` (posicional) + flags `--title` / `--domain` / `--tags`. Falla con exit ≠ 0 si falta el slug.
|
||||||
|
2. `slugify_ascii` — normaliza el slug a ASCII lowercase sin diacríticos (reutiliza la función del registry, solo stdlib).
|
||||||
|
3. `next_numbered_dir papers/` — calcula el siguiente NNNN de 4 dígitos sin colisión.
|
||||||
|
4. Crea `papers/<NNNN-slug>/` con las subcarpetas `experiments/ data/ figures/ reviews/ out/`.
|
||||||
|
5. Copia `docs/templates/paper.md` + `docs/templates/preregistration.md` y rellena el frontmatter por clave de línea (title, slug, date de hoy, domain, tags; phase=question y status=draft vienen de la plantilla).
|
||||||
|
6. Crea `references.md` vacío.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- **NO hace `git init`.** El paper arranca en fase interna local; `papers/` está gitignored en el repo padre (solo `papers/.gitkeep` se versiona). Promocionar a sub-repo Gitea (fase publishable) es manual.
|
||||||
|
- **El `--title` no debe contener el carácter `|`** (se usa como delimitador de sed al rellenar el frontmatter; los `&` y `\` sí se escapan).
|
||||||
|
- **No indexa el paper en `registry.db`** — los artefactos `papers/<slug>/` no se indexan en esta fase (KISS); sí se indexa este pipeline.
|
||||||
|
- Requiere `python3` (del venv del registry o del sistema) para slugificar; `slugify_ascii` solo usa stdlib, así que el venv no es obligatorio.
|
||||||
|
- Idempotencia: si el directorio destino ya existiera, aborta con exit ≠ 0 en vez de sobrescribir.
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
Cada paper es un artefacto independiente (mismo patrón que `apps/` y `analysis/`, pero para investigación). El pipeline usa `set -euo pipefail`: cualquier fallo detiene la ejecución. Parte del grupo de capacidad `papers` — diseño completo en `reports/0001-2026-06-30-papers-system-design.md`.
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# init_paper
|
||||||
|
# ----------
|
||||||
|
# Scaffold de un paper académico reproducible en papers/<NNNN-slug>/.
|
||||||
|
#
|
||||||
|
# Calcula el siguiente número incremental escaneando papers/, crea el
|
||||||
|
# directorio con todas las subcarpetas (experiments data figures reviews out),
|
||||||
|
# copia las plantillas paper.md + preregistration.md rellenando el frontmatter
|
||||||
|
# (title, slug, date de hoy, phase=question, status=draft) y crea references.md.
|
||||||
|
#
|
||||||
|
# NO hace `git init`: el paper arranca en fase interna local (papers/ está
|
||||||
|
# gitignored en el repo padre, solo .gitkeep se versiona). La promoción a
|
||||||
|
# sub-repo Gitea (fase publishable) es un paso posterior MANUAL.
|
||||||
|
#
|
||||||
|
# Compone: next_numbered_dir (helper de numeración del registry) +
|
||||||
|
# slugify_ascii (slug ASCII del registry).
|
||||||
|
#
|
||||||
|
# USO:
|
||||||
|
# ./init_paper.sh <slug> [--title "..."] [--domain <d>] [--tags a,b,c]
|
||||||
|
#
|
||||||
|
# EJEMPLOS:
|
||||||
|
# ./init_paper.sh mi-primer-paper --title "Mi primer paper"
|
||||||
|
# ./init_paper.sh reactive-loop-calls --domain datascience --tags registry,telemetria
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REGISTRY_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||||
|
|
||||||
|
# Funciones atómicas del registry
|
||||||
|
source "$REGISTRY_ROOT/bash/functions/io/next_numbered_dir.sh"
|
||||||
|
|
||||||
|
# ── Parsing de argumentos ────────────────────────────────────
|
||||||
|
|
||||||
|
SLUG_RAW=""
|
||||||
|
TITLE=""
|
||||||
|
DOMAIN="datascience"
|
||||||
|
TAGS=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--title)
|
||||||
|
TITLE="$2"; shift 2 ;;
|
||||||
|
--domain)
|
||||||
|
DOMAIN="$2"; shift 2 ;;
|
||||||
|
--tags)
|
||||||
|
TAGS="$2"; shift 2 ;;
|
||||||
|
-h|--help)
|
||||||
|
grep "^#" "$0" | sed 's/^# \?//' ; exit 0 ;;
|
||||||
|
-*)
|
||||||
|
echo "Flag desconocido: $1" >&2 ; exit 1 ;;
|
||||||
|
*)
|
||||||
|
if [ -z "$SLUG_RAW" ]; then
|
||||||
|
SLUG_RAW="$1"
|
||||||
|
else
|
||||||
|
echo "ERROR: argumento posicional inesperado: '$1' (solo se admite un <slug>)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$SLUG_RAW" ]; then
|
||||||
|
echo "ERROR: falta el argumento <slug>." >&2
|
||||||
|
echo "Uso: $0 <slug> [--title \"...\"] [--domain <d>] [--tags a,b,c]" >&2
|
||||||
|
echo " Ejemplo: $0 mi-primer-paper --title \"Mi primer paper\"" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Slugificar (reutiliza slugify_ascii del registry; solo stdlib) ──
|
||||||
|
|
||||||
|
PYBIN="$REGISTRY_ROOT/python/.venv/bin/python3"
|
||||||
|
[ -x "$PYBIN" ] || PYBIN="$(command -v python3 || true)"
|
||||||
|
if [ -z "$PYBIN" ]; then
|
||||||
|
echo "ERROR: no se encontró python3 para slugificar el slug." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SLUG_CLEAN=$("$PYBIN" -c '
|
||||||
|
import sys, os
|
||||||
|
sys.path.insert(0, os.path.join(sys.argv[2], "python", "functions"))
|
||||||
|
from core.slugify_ascii import slugify_ascii
|
||||||
|
print(slugify_ascii(sys.argv[1], default="paper"))
|
||||||
|
' "$SLUG_RAW" "$REGISTRY_ROOT")
|
||||||
|
|
||||||
|
# ── Resolver número incremental y directorio destino ─────────
|
||||||
|
|
||||||
|
PAPERS_DIR="$REGISTRY_ROOT/papers"
|
||||||
|
mkdir -p "$PAPERS_DIR"
|
||||||
|
|
||||||
|
NUM=$(next_numbered_dir "$PAPERS_DIR")
|
||||||
|
SLUG_FULL="${NUM}-${SLUG_CLEAN}"
|
||||||
|
PAPER_DIR="$PAPERS_DIR/$SLUG_FULL"
|
||||||
|
|
||||||
|
if [ -d "$PAPER_DIR" ]; then
|
||||||
|
echo "ERROR: el directorio del paper ya existe: $PAPER_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TODAY=$(date +%Y-%m-%d)
|
||||||
|
[ -n "$TITLE" ] || TITLE="$SLUG_CLEAN"
|
||||||
|
|
||||||
|
TAGS_YAML="[]"
|
||||||
|
if [ -n "$TAGS" ]; then
|
||||||
|
TAGS_YAML="[$(echo "$TAGS" | sed 's/,/, /g')]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════════"
|
||||||
|
echo " INIT PAPER: ${SLUG_FULL}"
|
||||||
|
echo " Título: ${TITLE}"
|
||||||
|
echo " Directorio: ${PAPER_DIR}"
|
||||||
|
echo "════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Crear estructura ─────────────────────────────────────────
|
||||||
|
|
||||||
|
echo "[1/3] Creando estructura..."
|
||||||
|
mkdir -p "$PAPER_DIR"/experiments "$PAPER_DIR"/data "$PAPER_DIR"/figures \
|
||||||
|
"$PAPER_DIR"/reviews "$PAPER_DIR"/out
|
||||||
|
echo " experiments/ data/ figures/ reviews/ out/"
|
||||||
|
|
||||||
|
# ── Copiar plantillas + rellenar frontmatter ─────────────────
|
||||||
|
|
||||||
|
echo "[2/3] Escribiendo paper.md + preregistration.md..."
|
||||||
|
|
||||||
|
# Escapa caracteres especiales del RHS de sed (delimitador |)
|
||||||
|
sed_escape() { printf '%s' "$1" | sed -e 's/[\\&|]/\\&/g'; }
|
||||||
|
TITLE_ESC="$(sed_escape "$TITLE")"
|
||||||
|
DOMAIN_ESC="$(sed_escape "$DOMAIN")"
|
||||||
|
|
||||||
|
PAPER_MD="$PAPER_DIR/paper.md"
|
||||||
|
PREREG_MD="$PAPER_DIR/preregistration.md"
|
||||||
|
|
||||||
|
cp "$REGISTRY_ROOT/docs/templates/paper.md" "$PAPER_MD"
|
||||||
|
cp "$REGISTRY_ROOT/docs/templates/preregistration.md" "$PREREG_MD"
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e "s|^title:.*|title: \"${TITLE_ESC}\"|" \
|
||||||
|
-e "s|^slug:.*|slug: ${SLUG_FULL}|" \
|
||||||
|
-e "s|^date:.*|date: ${TODAY}|" \
|
||||||
|
-e "s|^domain:.*|domain: ${DOMAIN_ESC}|" \
|
||||||
|
-e "s|^tags:.*|tags: ${TAGS_YAML}|" \
|
||||||
|
"$PAPER_MD"
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e "s|^paper_slug:.*|paper_slug: ${SLUG_FULL}|" \
|
||||||
|
"$PREREG_MD"
|
||||||
|
|
||||||
|
echo " $PAPER_MD"
|
||||||
|
echo " $PREREG_MD"
|
||||||
|
|
||||||
|
# ── references.md ────────────────────────────────────────────
|
||||||
|
|
||||||
|
echo "[3/3] Escribiendo references.md..."
|
||||||
|
cat > "$PAPER_DIR/references.md" << EOF
|
||||||
|
# References — ${TITLE}
|
||||||
|
|
||||||
|
<!-- Una entrada por referencia. Formato libre (o BibTeX) hasta promocionar a publishable. -->
|
||||||
|
EOF
|
||||||
|
echo " $PAPER_DIR/references.md"
|
||||||
|
|
||||||
|
# ── Resumen ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════════════════════"
|
||||||
|
echo " PAPER '${SLUG_FULL}' LISTO (fase: question, status: draft)"
|
||||||
|
echo "════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
echo " Pasos siguientes:"
|
||||||
|
echo " 1. Revisión de literatura (skill /deep-research) → Related work."
|
||||||
|
echo " 2. Pre-registro: congela H0/H1 + plan en preregistration.md (preregister_hypothesis)."
|
||||||
|
echo " 3. Experimentos en experiments/ → análisis (grupo eda) → escritura IMRaD en paper.md."
|
||||||
|
echo " 4. render_paper_pdf → out/paper.pdf. Peer review adversarial → reviews/."
|
||||||
|
echo ""
|
||||||
|
echo " papers/ está gitignored: este paper vive local hasta promocionar a publishable."
|
||||||
|
echo ""
|
||||||
+47
-31
@@ -18,6 +18,7 @@ type pyParam struct {
|
|||||||
Default string // empty if required
|
Default string // empty if required
|
||||||
IsKwargs bool // **kwargs
|
IsKwargs bool // **kwargs
|
||||||
IsRegistry bool // type is a registry type (needs factory)
|
IsRegistry bool // type is a registry type (needs factory)
|
||||||
|
KwOnly bool // declared after a bare "*" or "*args" — must be passed by keyword
|
||||||
}
|
}
|
||||||
|
|
||||||
// pyFactory links a registry type to the function that creates it.
|
// pyFactory links a registry type to the function that creates it.
|
||||||
@@ -45,12 +46,21 @@ func parsePySignature(sig string) []pyParam {
|
|||||||
// Split by comma, respecting nested brackets
|
// Split by comma, respecting nested brackets
|
||||||
parts := splitParams(raw)
|
parts := splitParams(raw)
|
||||||
var params []pyParam
|
var params []pyParam
|
||||||
|
kwOnly := false
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
part = strings.TrimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
if part == "" || part == "self" || part == "cls" {
|
if part == "" || part == "self" || part == "cls" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// A bare "*" (PEP 3102) or "*args" var-positional marks the start of
|
||||||
|
// keyword-only params. Neither maps cleanly to positional CLI args, so
|
||||||
|
// skip the marker itself and flag every following param as keyword-only.
|
||||||
|
if part == "*" || (strings.HasPrefix(part, "*") && !strings.HasPrefix(part, "**")) {
|
||||||
|
kwOnly = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
p := parseSingleParam(part)
|
p := parseSingleParam(part)
|
||||||
|
p.KwOnly = kwOnly
|
||||||
params = append(params, p)
|
params = append(params, p)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
@@ -189,11 +199,19 @@ func generatePyRunner(fn *registry.Function, db *registry.DB, registryRoot strin
|
|||||||
// Classify params
|
// Classify params
|
||||||
var factoryImports []string // import lines for factories
|
var factoryImports []string // import lines for factories
|
||||||
var factorySetup []string // code to create factory objects
|
var factorySetup []string // code to create factory objects
|
||||||
var argLines []string // code to parse CLI args
|
var bodyLines []string // code that fills _call_args / _call_kwargs
|
||||||
var callArgs []string // arguments to pass to the function
|
|
||||||
|
|
||||||
cliArgIdx := 0
|
cliArgIdx := 0
|
||||||
|
|
||||||
|
// emitCall appends one param to _call_args (positional) or _call_kwargs
|
||||||
|
// (keyword-only). indent prefixes the line (for params read inside an `if`).
|
||||||
|
emitCall := func(p pyParam, indent string) string {
|
||||||
|
if p.KwOnly {
|
||||||
|
return fmt.Sprintf("%s_call_kwargs[%q] = %s", indent, p.Name, p.Name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s_call_args.append(%s)", indent, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
if p.IsKwargs {
|
if p.IsKwargs {
|
||||||
// Skip **kwargs for now — can't auto-resolve from CLI
|
// Skip **kwargs for now — can't auto-resolve from CLI
|
||||||
@@ -235,27 +253,35 @@ func generatePyRunner(fn *registry.Function, db *registry.DB, registryRoot strin
|
|||||||
fmt.Sprintf("%s = %s(%s)", p.Name, factory.FuncName,
|
fmt.Sprintf("%s = %s(%s)", p.Name, factory.FuncName,
|
||||||
strings.Join(factoryArgs, ", ")))
|
strings.Join(factoryArgs, ", ")))
|
||||||
|
|
||||||
callArgs = append(callArgs, p.Name)
|
// Factory objects are always present (required).
|
||||||
|
bodyLines = append(bodyLines, emitCall(p, ""))
|
||||||
} else {
|
} else {
|
||||||
// Primitive type — from CLI args
|
// Primitive type — from CLI args.
|
||||||
if p.Default != "" {
|
if p.Default != "" {
|
||||||
// Optional param with default
|
// Optional: only pass when the CLI arg is present. When absent we
|
||||||
argLines = append(argLines,
|
// DON'T replicate the signature default (it may reference a module
|
||||||
fmt.Sprintf("%s = _args[%d] if len(_args) > %d else %s",
|
// constant that doesn't exist in this runner) — we simply omit the
|
||||||
p.Name, cliArgIdx, cliArgIdx, convertDefault(p.Type, p.Default)))
|
// argument so the function applies its own native default.
|
||||||
argLines = append(argLines,
|
bodyLines = append(bodyLines,
|
||||||
convertArg(p.Name, p.Type, true))
|
fmt.Sprintf("if len(_args) > %d:", cliArgIdx))
|
||||||
|
bodyLines = append(bodyLines,
|
||||||
|
fmt.Sprintf(" %s = _args[%d]", p.Name, cliArgIdx))
|
||||||
|
if conv := convertArg(p.Name, p.Type, true); conv != "" {
|
||||||
|
bodyLines = append(bodyLines, " "+conv)
|
||||||
|
}
|
||||||
|
bodyLines = append(bodyLines, emitCall(p, " "))
|
||||||
} else {
|
} else {
|
||||||
// Required param
|
// Required param.
|
||||||
argLines = append(argLines,
|
bodyLines = append(bodyLines,
|
||||||
fmt.Sprintf("if len(_args) <= %d: sys.exit('error: missing required arg: %s (%s)')",
|
fmt.Sprintf("if len(_args) <= %d: sys.exit('error: missing required arg: %s (%s)')",
|
||||||
cliArgIdx, p.Name, p.Type))
|
cliArgIdx, p.Name, p.Type))
|
||||||
argLines = append(argLines,
|
bodyLines = append(bodyLines,
|
||||||
fmt.Sprintf("%s = _args[%d]", p.Name, cliArgIdx))
|
fmt.Sprintf("%s = _args[%d]", p.Name, cliArgIdx))
|
||||||
argLines = append(argLines,
|
if conv := convertArg(p.Name, p.Type, false); conv != "" {
|
||||||
convertArg(p.Name, p.Type, false))
|
bodyLines = append(bodyLines, conv)
|
||||||
|
}
|
||||||
|
bodyLines = append(bodyLines, emitCall(p, ""))
|
||||||
}
|
}
|
||||||
callArgs = append(callArgs, p.Name)
|
|
||||||
cliArgIdx++
|
cliArgIdx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,18 +315,18 @@ func generatePyRunner(fn *registry.Function, db *registry.DB, registryRoot strin
|
|||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arg parsing
|
// Arg parsing — build the positional/keyword argument collections.
|
||||||
if len(argLines) > 0 {
|
|
||||||
sb.WriteString("# --- parse CLI args ---\n")
|
sb.WriteString("# --- parse CLI args ---\n")
|
||||||
for _, line := range argLines {
|
sb.WriteString("_call_args = []\n")
|
||||||
|
sb.WriteString("_call_kwargs = {}\n")
|
||||||
|
for _, line := range bodyLines {
|
||||||
sb.WriteString(line + "\n")
|
sb.WriteString(line + "\n")
|
||||||
}
|
}
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
}
|
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
sb.WriteString("# --- execute ---\n")
|
sb.WriteString("# --- execute ---\n")
|
||||||
sb.WriteString(fmt.Sprintf("_result = %s(%s)\n", fn.Name, strings.Join(callArgs, ", ")))
|
sb.WriteString(fmt.Sprintf("_result = %s(*_call_args, **_call_kwargs)\n", fn.Name))
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
@@ -365,16 +391,6 @@ func convertArg(name, typ string, _ bool) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertDefault ensures the default value is valid Python for the given type.
|
|
||||||
func convertDefault(_, def string) string {
|
|
||||||
// Most defaults from the signature are already valid Python
|
|
||||||
// Just handle the None case for Optional types
|
|
||||||
if def == "None" || def == "" {
|
|
||||||
return "None"
|
|
||||||
}
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
|
|
||||||
// pythonList creates a Python list literal from strings: ["a", "b", "c"]
|
// pythonList creates a Python list literal from strings: ["a", "b", "c"]
|
||||||
func pythonList(items []string) string {
|
func pythonList(items []string) string {
|
||||||
quoted := make([]string, len(items))
|
quoted := make([]string, len(items))
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"fn-registry/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signature with a bare "*" (PEP 3102) separating positional from keyword-only
|
||||||
|
// params. This is the shape that used to make fn run emit "* = _args[3]".
|
||||||
|
const kwOnlySig = "def add_event_dav(summary: str, start: str, end: str = '', *, location: str = '', all_day: bool = False) -> dict"
|
||||||
|
|
||||||
|
func TestParsePySignatureBareStarKeywordOnly(t *testing.T) {
|
||||||
|
params := parsePySignature(kwOnlySig)
|
||||||
|
|
||||||
|
// The bare "*" marker must never surface as a real parameter.
|
||||||
|
for _, p := range params {
|
||||||
|
if p.Name == "*" {
|
||||||
|
t.Fatalf("bare '*' leaked as a param: %+v", params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
want := map[string]bool{ // name -> expected KwOnly
|
||||||
|
"summary": false,
|
||||||
|
"start": false,
|
||||||
|
"end": false,
|
||||||
|
"location": true,
|
||||||
|
"all_day": true,
|
||||||
|
}
|
||||||
|
if len(params) != len(want) {
|
||||||
|
t.Fatalf("got %d params, want %d: %+v", len(params), len(want), params)
|
||||||
|
}
|
||||||
|
for _, p := range params {
|
||||||
|
kw, ok := want[p.Name]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unexpected param %q", p.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.KwOnly != kw {
|
||||||
|
t.Errorf("param %q KwOnly=%v, want %v", p.Name, p.KwOnly, kw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratePyRunnerKeywordOnlyValid(t *testing.T) {
|
||||||
|
fn := ®istry.Function{
|
||||||
|
Name: "add_event_dav",
|
||||||
|
Lang: "py",
|
||||||
|
FilePath: "python/functions/pipelines/add_event_dav.py",
|
||||||
|
Signature: kwOnlySig,
|
||||||
|
}
|
||||||
|
|
||||||
|
// All params are primitive, so no factory lookup happens and db is unused.
|
||||||
|
script, err := generatePyRunner(fn, nil, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generatePyRunner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(script, "* = _args") {
|
||||||
|
t.Fatalf("runner emitted invalid syntax '* = _args':\n%s", script)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The signature default DEFAULT_BASE_URL (a module constant) must NOT be
|
||||||
|
// replicated into the runner — that NameErrors at runtime.
|
||||||
|
if strings.Contains(script, "DEFAULT_BASE_URL") {
|
||||||
|
t.Errorf("runner replicated non-literal default DEFAULT_BASE_URL:\n%s", script)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required positionals are appended; keyword-only optionals go to kwargs.
|
||||||
|
for _, want := range []string{
|
||||||
|
"_call_args.append(summary)",
|
||||||
|
"_call_args.append(start)",
|
||||||
|
`_call_kwargs["location"] = location`,
|
||||||
|
`_call_kwargs["all_day"] = all_day`,
|
||||||
|
"_result = add_event_dav(*_call_args, **_call_kwargs)",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(script, want) {
|
||||||
|
t.Errorf("missing %q in generated runner:\n%s", want, script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The generated runner must itself be valid Python (compile, don't run).
|
||||||
|
mustCompilePython(t, script)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustCompilePython checks the script parses as valid Python via py_compile.
|
||||||
|
func mustCompilePython(t *testing.T, script string) {
|
||||||
|
t.Helper()
|
||||||
|
f, err := os.CreateTemp(t.TempDir(), "runner_*.py")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("temp file: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := f.WriteString(script); err != nil {
|
||||||
|
t.Fatalf("write: %v", err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
py := pythonBinForTest()
|
||||||
|
out, err := exec.Command(py, "-m", "py_compile", f.Name()).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generated runner is not valid Python (%s): %v\n%s", py, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pythonBinForTest prefers the project venv, falling back to python3 on PATH.
|
||||||
|
func pythonBinForTest() string {
|
||||||
|
for _, c := range []string{"../../python/.venv/bin/python3", "python3"} {
|
||||||
|
if c == "python3" {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(c); err == nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "python3"
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "*args" var-positional marker must behave like the bare "*": skipped, and
|
||||||
|
// everything after it treated as keyword-only.
|
||||||
|
func TestParsePySignatureVarargsKeywordOnly(t *testing.T) {
|
||||||
|
sig := "def f(a: str, *args, b: int = 0) -> dict"
|
||||||
|
params := parsePySignature(sig)
|
||||||
|
|
||||||
|
for _, p := range params {
|
||||||
|
if strings.HasPrefix(p.Name, "*") {
|
||||||
|
t.Fatalf("'*args' marker leaked as a param: %+v", params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(params) != 2 {
|
||||||
|
t.Fatalf("got %d params, want 2: %+v", len(params), params)
|
||||||
|
}
|
||||||
|
got := map[string]bool{}
|
||||||
|
for _, p := range params {
|
||||||
|
got[p.Name] = p.KwOnly
|
||||||
|
}
|
||||||
|
if got["a"] != false || got["b"] != true {
|
||||||
|
t.Errorf("KwOnly mismatch: a=%v (want false), b=%v (want true)", got["a"], got["b"])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0051 — Funciones pendientes del pipeline de extraccion (NER+RE+OpenIE)
|
# 0051 — Funciones pendientes del pipeline de extraccion (NER+RE+OpenIE)
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0054 — deploy_server: refactor registry-first (SSH/systemd/rsync/health/docker-compose)
|
# 0054 — deploy_server: refactor registry-first (SSH/systemd/rsync/health/docker-compose)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0055 — docker_tui: refactor para usar funciones docker_* del registry
|
# 0055 — docker_tui: refactor para usar funciones docker_* del registry
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0056 — audit_uses_functions: detectar imports Python anidados (`from pkg.subpkg import X`)
|
# 0056 — audit_uses_functions: detectar imports Python anidados (`from pkg.subpkg import X`)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0057 — audit_uses_functions: mejorar deteccion de simbolos Go con abreviaturas
|
# 0057 — audit_uses_functions: mejorar deteccion de simbolos Go con abreviaturas
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0060 — `fn doctor secrets`: scan de secrets en TODOS los repos
|
# 0060 — `fn doctor secrets`: scan de secrets en TODOS los repos
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0061 — Integrar `notify_telegram` en deploy_server + bucle reactivo
|
# 0061 — Integrar `notify_telegram` en deploy_server + bucle reactivo
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ domain:
|
|||||||
- registry-quality
|
- registry-quality
|
||||||
scope: registry-only
|
scope: registry-only
|
||||||
priority: alta
|
priority: alta
|
||||||
depends:
|
depends: ["0071f"]
|
||||||
- "0071f"
|
|
||||||
blocks: []
|
blocks: []
|
||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ domain:
|
|||||||
- registry-quality
|
- registry-quality
|
||||||
scope: registry-only
|
scope: registry-only
|
||||||
priority: media
|
priority: media
|
||||||
depends:
|
depends: ["0071f"]
|
||||||
- "0071f"
|
|
||||||
blocks: []
|
blocks: []
|
||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Contexto
|
## Contexto
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Contexto
|
## Contexto
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sintoma
|
## Sintoma
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sintoma
|
## Sintoma
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0100 — Migrar frontmatter inline a YAML canonico en dev/issues/
|
# 0100 — Migrar frontmatter inline a YAML canonico en dev/issues/
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ related:
|
|||||||
- "0103"
|
- "0103"
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: [slash-command, dispatch, type-aware]
|
tags: [slash-command, dispatch, type-aware, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0104 — `/fix-issue` type-aware dispatch
|
# 0104 — `/fix-issue` type-aware dispatch
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ related:
|
|||||||
- "0107"
|
- "0107"
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: [modules, versioning, codegen, fail-loud]
|
tags: [modules, versioning, codegen, fail-loud, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0107e — Version pinning + codegen fail-loud
|
# 0107e — Version pinning + codegen fail-loud
|
||||||
|
|||||||
@@ -15,12 +15,7 @@ related:
|
|||||||
- "0109"
|
- "0109"
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags:
|
tags: [ausente-ready, skill-tree, cpp, imgui, dashboard, gamification]
|
||||||
- skill-tree
|
|
||||||
- cpp
|
|
||||||
- imgui
|
|
||||||
- dashboard
|
|
||||||
- gamification
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0109k — Dashboard panel
|
# 0109k — Dashboard panel
|
||||||
|
|||||||
@@ -16,13 +16,7 @@ related:
|
|||||||
- "0106"
|
- "0106"
|
||||||
created: 2026-05-18
|
created: 2026-05-18
|
||||||
updated: 2026-05-18
|
updated: 2026-05-18
|
||||||
tags:
|
tags: [ausente-ready, service, go, http, issues, flows, api]
|
||||||
- service
|
|
||||||
- go
|
|
||||||
- http
|
|
||||||
- issues
|
|
||||||
- flows
|
|
||||||
- api
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0109m — issues_api service
|
# 0109m — issues_api service
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ related:
|
|||||||
- "0068"
|
- "0068"
|
||||||
created: 2026-05-18
|
created: 2026-05-18
|
||||||
updated: 2026-05-19
|
updated: 2026-05-19
|
||||||
tags: [e2e_checks, recopilador, batch, coverage, epic]
|
tags: [e2e_checks, recopilador, batch, coverage, epic, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Sub-issues
|
# Sub-issues
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ related:
|
|||||||
- "0068"
|
- "0068"
|
||||||
created: 2026-05-19
|
created: 2026-05-19
|
||||||
updated: 2026-05-19
|
updated: 2026-05-19
|
||||||
tags: [e2e_checks, recopilador, batch, design]
|
tags: [e2e_checks, recopilador, batch, design, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0121a — Design-e2e batch
|
# 0121a — Design-e2e batch
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ domain:
|
|||||||
- registry-quality
|
- registry-quality
|
||||||
scope: registry
|
scope: registry
|
||||||
priority: media
|
priority: media
|
||||||
depends:
|
depends: ["0121a"]
|
||||||
- "0121a"
|
|
||||||
- "0121b"
|
|
||||||
blocks:
|
blocks:
|
||||||
- "0122"
|
- "0122"
|
||||||
related:
|
related:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ related:
|
|||||||
- "0086"
|
- "0086"
|
||||||
created: 2026-05-18
|
created: 2026-05-18
|
||||||
updated: 2026-05-18
|
updated: 2026-05-18
|
||||||
tags: [revisor, mejorador, proposals, auto-apply, autonomous]
|
tags: [revisor, mejorador, proposals, auto-apply, autonomous, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0122 — fn-revisor + ampliar filtro auto-aplicable del orquestador
|
# 0122 — fn-revisor + ampliar filtro auto-aplicable del orquestador
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ related:
|
|||||||
- "0121a"
|
- "0121a"
|
||||||
created: 2026-05-19
|
created: 2026-05-19
|
||||||
updated: 2026-05-19
|
updated: 2026-05-19
|
||||||
tags: [dag_engine, cleanup, technical-debt]
|
tags: [dag_engine, cleanup, technical-debt, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0124 — dag_engine cleanup
|
# 0124 — dag_engine cleanup
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ related:
|
|||||||
- "0121a"
|
- "0121a"
|
||||||
created: 2026-05-19
|
created: 2026-05-19
|
||||||
updated: 2026-05-19
|
updated: 2026-05-19
|
||||||
tags: [deploy_server, cli, idempotency]
|
tags: [deploy_server, cli, idempotency, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0125 — deploy_server `--db` flag
|
# 0125 — deploy_server `--db` flag
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0128"
|
id: "0128"
|
||||||
title: "kanban: adjuntar archivos (drag&drop desc/chat + tab Archivos)"
|
title: "kanban: adjuntar archivos (drag&drop desc/chat + tab Archivos)"
|
||||||
status: in_progress
|
status: in-progress
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- apps-tools
|
- apps-tools
|
||||||
|
|||||||
@@ -13,12 +13,7 @@ blocks:
|
|||||||
- 0130b
|
- 0130b
|
||||||
related:
|
related:
|
||||||
- "0130"
|
- "0130"
|
||||||
tags:
|
tags: [registry, go, parser, frontmatter, fsnotify, ausente-ready]
|
||||||
- registry
|
|
||||||
- go
|
|
||||||
- parser
|
|
||||||
- frontmatter
|
|
||||||
- fsnotify
|
|
||||||
flow: "0130"
|
flow: "0130"
|
||||||
created: "2026-05-22"
|
created: "2026-05-22"
|
||||||
updated: "2026-05-22"
|
updated: "2026-05-22"
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ domain:
|
|||||||
- dev-ux
|
- dev-ux
|
||||||
scope: app-scoped
|
scope: app-scoped
|
||||||
priority: alta
|
priority: alta
|
||||||
depends:
|
depends: ["0130a"]
|
||||||
- "0130a"
|
|
||||||
blocks:
|
blocks:
|
||||||
- "0130c"
|
- "0130c"
|
||||||
related:
|
related:
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
id: "0134"
|
id: "0134"
|
||||||
title: "Mesh protocol spec: capability manifests, ed25519 envelopes, enrollment, audit chain"
|
title: "Mesh protocol spec: capability manifests, ed25519 envelopes, enrollment, audit chain"
|
||||||
status: pending
|
status: pendiente
|
||||||
type: spec
|
type: spec
|
||||||
domain:
|
domain:
|
||||||
- infra
|
- infra
|
||||||
- cybersecurity
|
- cybersecurity
|
||||||
- protocols
|
- protocols
|
||||||
scope: cross-app
|
scope: cross-app
|
||||||
priority: high
|
priority: alta
|
||||||
depends: []
|
depends: []
|
||||||
blocks:
|
blocks:
|
||||||
- "0135"
|
- "0135"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0144"
|
id: "0144"
|
||||||
title: "Agent LLM per machine (user + sudo) con tool registry y mesh dispatch"
|
title: "Agent LLM per machine (user + sudo) con tool registry y mesh dispatch"
|
||||||
status: pending
|
status: pendiente
|
||||||
type: spec
|
type: spec
|
||||||
domain:
|
domain:
|
||||||
- agents
|
- agents
|
||||||
@@ -9,7 +9,7 @@ domain:
|
|||||||
- infra
|
- infra
|
||||||
- cybersecurity
|
- cybersecurity
|
||||||
scope: multi-app
|
scope: multi-app
|
||||||
priority: high
|
priority: alta
|
||||||
depends:
|
depends:
|
||||||
- "0134"
|
- "0134"
|
||||||
- "0140"
|
- "0140"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0146"
|
id: "0146"
|
||||||
title: "add-pc one-shot: añade PC al mesh + agente LLM en <2min desde movil"
|
title: "add-pc one-shot: añade PC al mesh + agente LLM en <2min desde movil"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0009"]
|
related_flows: ["0009"]
|
||||||
related_issues: ["0134", "0144", "0145"]
|
related_issues: ["0134", "0144", "0145"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0147"
|
id: "0147"
|
||||||
title: "matrix-client-pc scaffold: Wails + React+Mantine + login MAS"
|
title: "matrix-client-pc scaffold: Wails + React+Mantine + login MAS"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0148", "0162"]
|
related_issues: ["0148", "0162"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0148"
|
id: "0148"
|
||||||
title: "matrix-client-pc rooms list + timeline con sync incremental"
|
title: "matrix-client-pc rooms list + timeline con sync incremental"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0147", "0149"]
|
related_issues: ["0147", "0149"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0149"
|
id: "0149"
|
||||||
title: "matrix-client-pc composer: markdown, reply, edit, reactions, media"
|
title: "matrix-client-pc composer: markdown, reply, edit, reactions, media"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0148", "0150"]
|
related_issues: ["0148", "0150"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0150"
|
id: "0150"
|
||||||
title: "matrix-client-pc E2EE: cross-signing, SAS verification, recovery"
|
title: "matrix-client-pc E2EE: cross-signing, SAS verification, recovery"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: critical
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0149", "0151"]
|
related_issues: ["0149", "0151"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0151"
|
id: "0151"
|
||||||
title: "matrix-client-pc calls LiveKit: 1:1 + grupales, mic/cam/screen"
|
title: "matrix-client-pc calls LiveKit: 1:1 + grupales, mic/cam/screen"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0150", "0152"]
|
related_issues: ["0150", "0152"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0152"
|
id: "0152"
|
||||||
title: "matrix-client-pc mini-webapps embebidas: Matrix Widget API v2"
|
title: "matrix-client-pc mini-webapps embebidas: Matrix Widget API v2"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010"]
|
related_flows: ["0010"]
|
||||||
related_issues: ["0151", "0153"]
|
related_issues: ["0151", "0153"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0154"
|
id: "0154"
|
||||||
title: "matrix-client-android scaffold: Kotlin + Compose + login MAS"
|
title: "matrix-client-android scaffold: Kotlin + Compose + login MAS"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0155", "0162"]
|
related_issues: ["0155", "0162"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0155"
|
id: "0155"
|
||||||
title: "matrix-client-android rooms list + timeline Compose"
|
title: "matrix-client-android rooms list + timeline Compose"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0154", "0156"]
|
related_issues: ["0154", "0156"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0156"
|
id: "0156"
|
||||||
title: "matrix-client-android composer: markdown, replies, edits, reactions, media"
|
title: "matrix-client-android composer: markdown, replies, edits, reactions, media"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0155", "0157"]
|
related_issues: ["0155", "0157"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0157"
|
id: "0157"
|
||||||
title: "matrix-client-android E2EE rust-sdk: cross-signing, SAS, recovery"
|
title: "matrix-client-android E2EE rust-sdk: cross-signing, SAS, recovery"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: critical
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0156", "0158"]
|
related_issues: ["0156", "0158"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0158"
|
id: "0158"
|
||||||
title: "matrix-client-android calls LiveKit nativo: mic/cam/screen + PiP"
|
title: "matrix-client-android calls LiveKit nativo: mic/cam/screen + PiP"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0157", "0159", "0161"]
|
related_issues: ["0157", "0159", "0161"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0159"
|
id: "0159"
|
||||||
title: "matrix-client-android push FCM via sygnal + Firebase setup"
|
title: "matrix-client-android push FCM via sygnal + Firebase setup"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0158", "0160"]
|
related_issues: ["0158", "0160"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0160"
|
id: "0160"
|
||||||
title: "matrix-client-android mini-webapps: WebView + Widget API v2 bridge"
|
title: "matrix-client-android mini-webapps: WebView + Widget API v2 bridge"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: medium
|
priority: media
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0159", "0161"]
|
related_issues: ["0159", "0161"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0161"
|
id: "0161"
|
||||||
title: "matrix-client-android foreground service: calls + lifecycle + lockscreen"
|
title: "matrix-client-android foreground service: calls + lifecycle + lockscreen"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: high
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0011"]
|
related_flows: ["0011"]
|
||||||
related_issues: ["0158", "0160"]
|
related_issues: ["0158", "0160"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0162"
|
id: "0162"
|
||||||
title: "Matrix: migrar Synapse a MAS como unico auth provider (MSC3861)"
|
title: "Matrix: migrar Synapse a MAS como unico auth provider (MSC3861)"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: critical
|
priority: alta
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010", "0011"]
|
related_flows: ["0010", "0011"]
|
||||||
related_issues: ["0147", "0154", "0163"]
|
related_issues: ["0147", "0154", "0163"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
id: "0163"
|
id: "0163"
|
||||||
title: "Matrix admin panel propio: users, rooms, devices, sessions (sustituye synapse-admin)"
|
title: "Matrix admin panel propio: users, rooms, devices, sessions (sustituye synapse-admin)"
|
||||||
status: pending
|
status: pendiente
|
||||||
priority: medium
|
priority: media
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010", "0011"]
|
related_flows: ["0010", "0011"]
|
||||||
related_issues: ["0162", "0147"]
|
related_issues: ["0162", "0147"]
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
id: "0179"
|
||||||
|
title: "dev_console: escaneo recursivo de dev/issues/ (subcarpetas por dominio)"
|
||||||
|
status: in-progress
|
||||||
|
type: bugfix
|
||||||
|
domain:
|
||||||
|
- meta
|
||||||
|
scope: app-scoped
|
||||||
|
priority: media
|
||||||
|
depends: []
|
||||||
|
blocks: []
|
||||||
|
related: []
|
||||||
|
created: 2026-06-30
|
||||||
|
updated: 2026-06-30
|
||||||
|
tags: [ausente-ready]
|
||||||
|
---
|
||||||
|
# 0179 — dev_console: escaneo recursivo de dev/issues/
|
||||||
|
|
||||||
|
## Contexto
|
||||||
|
|
||||||
|
Los issues activos se reorganizaron en subcarpetas por dominio dentro de `dev/issues/` (`kanban/`, `trading/`, `gamedev/`, `cpp/`, `matrix/`, `imagegen/`) para descongestionar el listado plano. El skill `/issue` ya se actualizó a glob recursivo (`dev/issues/**/*.md`, excluyendo `completed/`). Falta alinear el binario `dev_console`, que carga los issues con `LoadAllIssues(root)` / `LoadOpenIssues(root)` en `apps/dev_console/` y hoy no recorre subcarpetas — por lo que no ve los 49 issues movidos.
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Que `dev_console issue list/board/work` y los flujos que dependen de `LoadAllIssues`/`LoadOpenIssues` recorran `dev/issues/` de forma recursiva, excluyendo `dev/issues/completed/`, manteniendo el resto del comportamiento idéntico.
|
||||||
|
|
||||||
|
## Tareas
|
||||||
|
|
||||||
|
- [ ] Localizar la implementación de `LoadAllIssues` / `LoadOpenIssues` en `apps/dev_console/` (probable `parser.go` o equivalente).
|
||||||
|
- [ ] Cambiar el escaneo a `filepath.WalkDir` (o glob recursivo) bajo `dev/issues/`, saltando el directorio `completed/`.
|
||||||
|
- [ ] Mantener el orden de salida estable (ordenar por `id`).
|
||||||
|
- [ ] Recompilar el binario en el sub-repo de `dev_console` siguiendo TBD (`issue/0179-...`).
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Golden: lista incluye subcarpetas | e2e | `./apps/dev_console/dev_console issue list` | Aparecen issues de `cpp/`, `kanban/`, `trading/`, etc. (>= 49 que antes faltaban) |
|
||||||
|
| Edge: excluye completed/ | e2e | `dev_console issue list` | Ningún issue con `status: completado` de `completed/` aparece en el listado activo |
|
||||||
|
| Edge: conteo total coincide con /issue | e2e | comparar conteo con el glob recursivo de `/issue` | Mismo total de activos |
|
||||||
|
| Error: dev/issues vacío o ausente | unit | run en dir sin `dev/issues/` | Error claro, no panic |
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
Hermano del cambio ya hecho en `.claude/commands/issue.md` (glob `**/*.md`). Hasta cerrar este issue, usar `/issue` (no `dev_console`) para vistas completas del backlog.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
id: "0180"
|
||||||
|
title: "Modo ausente sobre la cola de issues: parametrizar /ausente + DAG dag_engine + validación"
|
||||||
|
status: pendiente
|
||||||
|
type: infra
|
||||||
|
domain:
|
||||||
|
- meta
|
||||||
|
scope: multi-app
|
||||||
|
priority: alta
|
||||||
|
depends: ["0179"]
|
||||||
|
blocks: []
|
||||||
|
related: []
|
||||||
|
created: 2026-06-30
|
||||||
|
updated: 2026-06-30
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
# 0180 — Modo ausente sobre la cola de issues (parametrizar /ausente + DAG + validación)
|
||||||
|
|
||||||
|
## Contexto
|
||||||
|
|
||||||
|
Modelo de colaboración acordado (ver memoria `modelo-colaboracion-ausente`): durante la jornada de oficina (L–J 10–14 / 15–19, V 10–16) y la noche (01–09), Claude trabaja en `/ausente` la cola de issues `ausente-ready` (39 issues hoy), sin supervisión. La curación del backlog ya está hecha (triage, taxonomía, deps de series formalizadas, tag `ausente-ready`).
|
||||||
|
|
||||||
|
Faltan 3 piezas para automatizarlo de forma segura.
|
||||||
|
|
||||||
|
## Problemas a resolver
|
||||||
|
|
||||||
|
1. **`/ausente` está acoplado al roadmap ComfyUI.** El skill (`.claude/commands/ausente.md`) hardcodea su backlog a funciones ComfyUI (secciones "Configuración" y "Backlog del roadmap ComfyUI"). Hay que **parametrizar la fuente de tareas** para que pueda tomar la cola de issues: la siguiente tarea = primer issue de `/issue list -t ausente-ready` cuyas `depends` estén todas en `completed/`, re-cruzando deps en cada ciclo (un issue se libera cuando su dep se cierra).
|
||||||
|
2. **Lanzamiento headless desde dag_engine.** `dag_engine` ejecuta steps (command/script/function), no abre una sesión Claude interactiva. Hay que resolver cómo un step arranca una sesión `role=orchestrator` en modo `/ausente` (candidatos: `launch_claude_agent_kitty_bash_infra` con DISPLAY, o `spawn_fleet_agent_bash_infra` si hay sesión tmux fleet) con el prompt autónomo + presupuesto.
|
||||||
|
3. **Presupuesto conservador aplicado.** Tope: 1–2 ejecutores concurrentes, solo issues S/M, ~1M tokens por franja, parada al llegar. Materializar el tope de tokens (hoy `orchestration.md` solo fija fan-out=6).
|
||||||
|
|
||||||
|
## Schedule objetivo (cuando se active)
|
||||||
|
|
||||||
|
- Inicio de franjas de oficina: `0 10 * * 1-5` (10:00 L–V) y `0 15 * * 1-4` (15:00 L–J, tras comida).
|
||||||
|
- Nocturno: `0 1 * * *` (01:00 diario).
|
||||||
|
- El modo, una vez lanzado, itera con `ScheduleWakeup` hasta que el humano vuelve (para al recibir prompt humano).
|
||||||
|
|
||||||
|
Borrador del DAG: `apps/dag_engine/dags/ausente-issues-queue.yaml` (creado como DRAFT sin schedule activo).
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Golden: corrida manual | e2e | lanzar `/ausente` con backlog=issues sobre 1 issue S de la cola | Coge el issue, lo implementa en worktree/sub-repo aislado, cierra DoD verde (golden+edge+error), push, bitácora actualizada |
|
||||||
|
| Edge: dep no satisfecha | e2e | cola con un issue cuya `depends` sigue activa | NO lo coge; pasa al siguiente arrancable |
|
||||||
|
| Edge: flota llena | e2e | 2 ejecutores activos (tope conservador) | Encola el resto, no lanza el 3.º |
|
||||||
|
| Error: presupuesto agotado | e2e | tope de tokens alcanzado | Para limpio, deja bitácora con lo pendiente, no deja agentes huérfanos |
|
||||||
|
| Vida útil | observabilidad | tras activar cron, 1 semana | Issues cerrados/semana > 0, 0 merges rotos a master, bitácora legible |
|
||||||
|
|
||||||
|
## Plan
|
||||||
|
|
||||||
|
1. Cerrar `0179` (dev_console recursivo) — dependencia.
|
||||||
|
2. Parametrizar `/ausente` (fuente de backlog = issues ausente-ready | roadmap; pasar la fuente al invocar).
|
||||||
|
3. Resolver el step de lanzamiento headless + presupuesto de tokens.
|
||||||
|
4. **Validación manual** (golden + edges) antes de activar el cron.
|
||||||
|
5. Activar schedule en el DAG + `systemctl --user restart dag_engine.service` con `--scheduler`.
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
Este issue NO es `ausente-ready` a propósito: requiere decisiones de diseño humanas (mecanismo de lanzamiento, forma del presupuesto) y toca el propio sistema que orquesta el modo ausente. Se hace JUNTOS, no desatendido.
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0059"
|
id: "0059"
|
||||||
title: "Resolver doble tracking de `apps/*/app.md` (fn_registry + sub-repo)"
|
title: "Resolver doble tracking de `apps/*/app.md` (fn_registry + sub-repo)"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: infra
|
type: infra
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "55"
|
id: "55"
|
||||||
title: "Roadmap de prereqs — issues de osint_graph que odr_console necesita antes/durante MVP"
|
title: "Roadmap de prereqs — issues de osint_graph que odr_console necesita antes/durante MVP"
|
||||||
status: pendiente
|
status: deferred
|
||||||
type: epic
|
type: epic
|
||||||
domain:
|
domain:
|
||||||
- osint
|
- osint
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0087"
|
id: "0087"
|
||||||
title: "Capability Discovery Acceleration"
|
title: "Capability Discovery Acceleration"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- meta
|
- meta
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0096"
|
id: "0096"
|
||||||
title: "Estandarizar ubicacion de apps: fuera de carpetas por lenguaje"
|
title: "Estandarizar ubicacion de apps: fuera de carpetas por lenguaje"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- apps-infra
|
- apps-infra
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0101"
|
id: "0101"
|
||||||
title: "dev_console Go binario: /issue /flow /work unificados"
|
title: "dev_console Go binario: /issue /flow /work unificados"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: app
|
type: app
|
||||||
domain:
|
domain:
|
||||||
- meta
|
- meta
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0103"
|
id: "0103"
|
||||||
title: "Taxonomia + slash commands /issue /flow /work"
|
title: "Taxonomia + slash commands /issue /flow /work"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- meta
|
- meta
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0105"
|
id: "0105"
|
||||||
title: "Estandarizar bloque service: en app.md + indexer + fn doctor services-spec"
|
title: "Estandarizar bloque service: en app.md + indexer + fn doctor services-spec"
|
||||||
status: in-progress
|
status: completado
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- meta
|
- meta
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0109g"
|
id: "0109g"
|
||||||
title: "skill_tree: panel terminal embebida (claude TUI dentro de la app)"
|
title: "skill_tree: panel terminal embebida (claude TUI dentro de la app)"
|
||||||
status: pendiente
|
status: deferred
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- meta
|
- meta
|
||||||
@@ -19,7 +19,7 @@ related:
|
|||||||
- "0102"
|
- "0102"
|
||||||
created: 2026-05-18
|
created: 2026-05-18
|
||||||
updated: 2026-05-18
|
updated: 2026-05-18
|
||||||
tags: [dod, evidence, frontmatter, taxonomy, validator]
|
tags: [dod, evidence, frontmatter, taxonomy, validator, ausente-ready]
|
||||||
flow: "0008"
|
flow: "0008"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ blocks:
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-22
|
created: 2026-05-22
|
||||||
updated: 2026-05-22
|
updated: 2026-05-22
|
||||||
tags: [agents_and_robots, http, sse, apikey, traefik, systemd]
|
tags: [agents_and_robots, http, sse, apikey, traefik, systemd, ausente-ready]
|
||||||
dod_evidence_schema:
|
dod_evidence_schema:
|
||||||
- id: build_ok
|
- id: build_ok
|
||||||
kind: cmd
|
kind: cmd
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0153"
|
id: "0153"
|
||||||
title: "matrix-client-pc agent integration: paneles para rooms operados por agentes"
|
title: "matrix-client-pc agent integration: paneles para rooms operados por agentes"
|
||||||
status: pending
|
status: deferred
|
||||||
priority: medium
|
priority: medium
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0010", "0009"]
|
related_flows: ["0010", "0009"]
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0164"
|
id: "0164"
|
||||||
title: "Bots agents_and_robots: cryptohelper.Init() cuelga al habilitar encryption=true"
|
title: "Bots agents_and_robots: cryptohelper.Init() cuelga al habilitar encryption=true"
|
||||||
status: pending
|
status: deferred
|
||||||
priority: high
|
priority: high
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
related_flows: ["0009"]
|
related_flows: ["0009"]
|
||||||
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: ["0167", "0168"]
|
related: ["0167", "0168"]
|
||||||
created: 2026-05-24
|
created: 2026-05-24
|
||||||
updated: 2026-05-24
|
updated: 2026-05-24
|
||||||
tags: [matrix, livekit, webrtc, turn, nat]
|
tags: [matrix, livekit, webrtc, turn, nat, ausente-ready]
|
||||||
---
|
---
|
||||||
# 0166 — Desplegar TURN para LiveKit (coturn o integrado)
|
# 0166 — Desplegar TURN para LiveKit (coturn o integrado)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0171"
|
id: "0171"
|
||||||
title: "Manifest de sub-repos por project + re-clonado y auditoría de cobertura en Gitea"
|
title: "Manifest de sub-repos por project + re-clonado y auditoría de cobertura en Gitea"
|
||||||
status: pendiente
|
status: completado
|
||||||
type: enhancement
|
type: enhancement
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0173"
|
id: "0173"
|
||||||
title: "EDA: bugs críticos de correctitud estadística (outlier_pct ×100, distribution_type por-skew)"
|
title: "EDA: bugs críticos de correctitud estadística (outlier_pct ×100, distribution_type por-skew)"
|
||||||
status: resuelto
|
status: completado
|
||||||
type: bugfix
|
type: bugfix
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0174"
|
id: "0174"
|
||||||
title: "EDA series temporales: período estacional roto + correlación de niveles + to_returns ciego"
|
title: "EDA series temporales: período estacional roto + correlación de niveles + to_returns ciego"
|
||||||
status: resuelto
|
status: completado
|
||||||
type: bugfix
|
type: bugfix
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0175"
|
id: "0175"
|
||||||
title: "EDA relational: precisión de FK inference (falsos positivos) + filtrar VIEWs + test ATTACH"
|
title: "EDA relational: precisión de FK inference (falsos positivos) + filtrar VIEWs + test ATTACH"
|
||||||
status: resuelto
|
status: completado
|
||||||
type: bugfix
|
type: bugfix
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0176"
|
id: "0176"
|
||||||
title: "EDA render: models/series/caveats en markdown+PDF + PDF para profile_database"
|
title: "EDA render: models/series/caveats en markdown+PDF + PDF para profile_database"
|
||||||
status: resuelto
|
status: completado
|
||||||
type: feature
|
type: feature
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: "0177"
|
id: "0177"
|
||||||
title: "EDA tipos: id secuencial fuera de correlación/PCA + η² espurio por cardinalidad + re-expresión no-continuas"
|
title: "EDA tipos: id secuencial fuera de correlación/PCA + η² espurio por cardinalidad + re-expresión no-continuas"
|
||||||
status: resuelto
|
status: completado
|
||||||
type: bugfix
|
type: bugfix
|
||||||
domain:
|
domain:
|
||||||
- registry-quality
|
- registry-quality
|
||||||
+1
-1
@@ -12,7 +12,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-17
|
created: 2026-05-17
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
# 0033 — C++ http_inspector + websocket_client
|
# 0033 — C++ http_inspector + websocket_client
|
||||||
|
|
||||||
+1
-4
@@ -13,10 +13,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-10
|
created: 2026-05-10
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags:
|
tags: [ausente-ready, gamedev, cpp, wasm]
|
||||||
- gamedev
|
|
||||||
- cpp
|
|
||||||
- wasm
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Objetivo
|
## Objetivo
|
||||||
+1
-1
@@ -13,7 +13,7 @@ blocks: []
|
|||||||
related: []
|
related: []
|
||||||
created: 2026-05-13
|
created: 2026-05-13
|
||||||
updated: 2026-05-17
|
updated: 2026-05-17
|
||||||
tags: []
|
tags: [ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Objetivo
|
## Objetivo
|
||||||
+1
-1
@@ -15,7 +15,7 @@ related:
|
|||||||
- "0106"
|
- "0106"
|
||||||
created: 2026-05-18
|
created: 2026-05-18
|
||||||
updated: 2026-05-18
|
updated: 2026-05-18
|
||||||
tags: [http, cpp, registry-gap, curl, helper]
|
tags: [http, cpp, registry-gap, curl, helper, ausente-ready]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 0110 — Helper HTTP cliente C++ en el registry
|
# 0110 — Helper HTTP cliente C++ en el registry
|
||||||
+1
-2
@@ -8,8 +8,7 @@ domain:
|
|||||||
- dev-ux
|
- dev-ux
|
||||||
scope: app-scoped
|
scope: app-scoped
|
||||||
priority: alta
|
priority: alta
|
||||||
depends:
|
depends: ["0130b"]
|
||||||
- "0130b"
|
|
||||||
blocks: []
|
blocks: []
|
||||||
related:
|
related:
|
||||||
- "0130"
|
- "0130"
|
||||||
+1
-1
@@ -16,7 +16,7 @@ related:
|
|||||||
- "0131"
|
- "0131"
|
||||||
created: 2026-05-22
|
created: 2026-05-22
|
||||||
updated: 2026-05-22
|
updated: 2026-05-22
|
||||||
tags: [cpp, imgui, terminal, pty, module]
|
tags: [cpp, imgui, terminal, pty, module, ausente-ready]
|
||||||
flow: ""
|
flow: ""
|
||||||
---
|
---
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user