Propósito
Tabla de datos centralizada que almacena el estado actual (snapshot) de frescura de las dependencias de CreaRack-Pro y CreaRackSL-workspace: versión pineada, instalada y última disponible en PyPI/npm, el desfase semver, los avisos de seguridad OSV.dev y — desde el “motor fiable” (task #275) — de dónde sale realmente la versión “en uso”, si la horquilla del manifiesto pone techo, y si la fila está retenida a propósito.
No es histórica: cada POST /api/tools/deps borra el snapshot anterior y publica uno nuevo. Sirve para responder “¿qué hay viejo hoy, y es de fiar el dato?” en la tool /tools/deps.
Migraciones
| Migración | Fecha | Qué añade |
|---|---|---|
0042_create_deps_freshness.sql | 2026-05-28 | Tabla base: repo, ecosystem, manifest, package, pinned, installed, latest, lag_kind, is_dev, checked_at. |
0048_deps_freshness_vulns.sql | 2026-07-11 | Eje de seguridad OSV.dev: vuln_count, vuln_ids, vuln_severity. |
0052_deps_freshness_motor_fiable.sql | 2026-08-28 | Motor fiable (task #275): manifests, resolved, installed_source, unbounded, unbounded_major, fix_within_spec. |
0053_deps_freshness_holds.sql | 2026-08-28 | Retenciones deliberadas (task #275): hold_reason, hold_since, hold_ref, hold_until. |
wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/<archivo>.sql
Schema (estado actual, tras 0042 + 0048 + 0052 + 0053)
CREATE TABLE deps_freshness (
id INTEGER PRIMARY KEY AUTOINCREMENT,
repo TEXT NOT NULL, -- 'crearack-pro' | 'workspace'
ecosystem TEXT NOT NULL, -- 'pypi' | 'npm'
manifest TEXT NOT NULL, -- manifiesto ganador tras deduplicar (el más restrictivo)
package TEXT NOT NULL,
pinned TEXT,
installed TEXT,
latest TEXT,
lag_kind TEXT NOT NULL DEFAULT 'unknown' CHECK (lag_kind IN (
'major', 'minor', 'patch', 'uptodate', 'unknown'
)),
is_dev INTEGER NOT NULL DEFAULT 0,
checked_at TEXT NOT NULL DEFAULT (datetime('now')),
-- 0048 — eje de seguridad OSV.dev
vuln_count INTEGER NOT NULL DEFAULT 0,
vuln_ids TEXT, -- ids OSV separados por coma (GHSA-..., PYSEC-...)
vuln_severity TEXT, -- peor severidad: critical|high|moderate|low|unknown
-- 0052 — motor fiable (task #275)
manifests TEXT, -- TODOS los manifiestos donde aparece el paquete, separados por coma
resolved TEXT, -- mayor versión que la horquilla `pinned` admite hoy
installed_source TEXT, -- 'prod' | 'runner' | 'lockfile' — de dónde sale `installed`
unbounded INTEGER NOT NULL DEFAULT 0, -- 1 = la horquilla no pone techo superior
unbounded_major INTEGER NOT NULL DEFAULT 0, -- 1 = sin techo Y ya corre en major distinto al declarado
fix_within_spec INTEGER, -- tri-estado: 1 basta redeploy · 0 cambiar pin · NULL sin fix publicado
-- 0053 — retenciones deliberadas (task #275)
hold_reason TEXT, -- por qué NO se actualiza, en llano. No nulo = fila retenida
hold_since TEXT, -- desde cuándo (YYYY-MM-DD; el handler descarta otro formato)
hold_ref TEXT, -- dónde vive la decisión ("task #204 · redis-py #2807")
hold_until TEXT -- cuándo revisarla (fecha o condición, texto libre)
);
CREATE INDEX idx_deps_freshness_lag ON deps_freshness(lag_kind);
CREATE INDEX idx_deps_freshness_repo ON deps_freshness(repo, ecosystem);
installed_source no lleva CHECK (SQLite/D1 no lo valida de forma fiable en un ADD COLUMN): el enum 'prod' | 'runner' | 'lockfile' se filtra en el handler (functions/api/tools/deps/index.ts), no en el schema.
Columnas detalladas
| Nombre | Tipo | Nulabilidad | Descripción |
|---|---|---|---|
id | INTEGER | NO | Primary key autoincrementado. |
repo | TEXT | NO | 'crearack-pro' | 'workspace'. |
ecosystem | TEXT | NO | 'pypi' | 'npm'. |
manifest | TEXT | NO | Manifiesto ganador (el más restrictivo) tras deduplicar entre los de manifests. |
package | TEXT | NO | Nombre del paquete. |
pinned | TEXT | SÍ | Especificación pineada tal como está en el manifiesto ('==6.0.4', '^9.3.0'…). |
installed | TEXT | SÍ | Versión resuelta/instalada — ver installed_source para saber de dónde sale. |
latest | TEXT | SÍ | Última disponible en PyPI/npm. |
lag_kind | TEXT | NO | 'major' | 'minor' | 'patch' | 'uptodate' | 'unknown'. Default 'unknown'. |
is_dev | INTEGER | NO | 1 = devDependency/herramienta de dev. |
checked_at | TEXT | NO | Timestamp ISO 8601 del snapshot. |
vuln_count | INTEGER | NO | Nº de avisos OSV.dev abiertos para la versión installed. Default 0. |
vuln_ids | TEXT | SÍ | Ids OSV separados por coma (GHSA-..., PYSEC-...). |
vuln_severity | TEXT | SÍ | Peor severidad entre los avisos: critical|high|moderate|low|unknown. |
manifests | TEXT | SÍ | Todos los manifiestos donde aparece el paquete, separados por coma (puede haber más de uno con horquillas distintas). |
resolved | TEXT | SÍ | Mayor versión publicada que la horquilla de pinned admite — la que se instalaría hoy con un despliegue limpio. |
installed_source | TEXT | SÍ | 'prod' (contenedor de producción vía [[entity—api—endpoint—installed-packages]], fuente de verdad) | 'runner' (resuelto por el runner de CI) | 'lockfile'. |
unbounded | INTEGER | NO | 1 = la horquilla no pone techo superior (>=X, ^X en npm…). Default 0. |
unbounded_major | INTEGER | NO | 1 = sin techo y ya corre en un major distinto al declarado (bajo 0.x el minor cuenta como major). Default 0. |
fix_within_spec | INTEGER | SÍ (tri-estado) | Solo con avisos de seguridad: 1 = hay versión con fix dentro de la horquilla (basta redeploy) · 0 = hay que cambiar el pin · NULL = OSV no publica versión con fix. |
hold_reason | TEXT | SÍ | Por qué la fila está parada A PROPÓSITO, en llano (1-2 frases). No nulo = fila retenida. Declarado en deps_holds.json (CreaRack-Pro). |
hold_since | TEXT | SÍ | Desde cuándo está retenida, YYYY-MM-DD. El handler (functions/api/tools/deps/index.ts) descarta cualquier otro formato → NULL. |
hold_ref | TEXT | SÍ | Dónde vive la decisión: tarea, issue del upstream, PR ("task #204 · redis-py #2807"). |
hold_until | TEXT | SÍ | Cuándo toca revisarla: fecha o condición en texto libre ("cuando salga redis-py 6"). |
Índices
idx_deps_freshness_lag: sobre(lag_kind).idx_deps_freshness_repo: sobre(repo, ecosystem).
Semántica
pinned vs. resolved vs. installed vs. latest
pinned: especificación declarada en el manifiesto, sin resolver ("^1.5.0").resolved: mayor versión que esa horquilla admite hoy, calculada por el colector — lo que se instalaría en un despliegue limpio.installed: versión que de verdad corre, segúninstalled_source(PROD real, runner de CI, o lockfile).latest: última disponible en el registro oficial, sin filtrar por la horquilla.
Cuando resolved e installed difieren, la UI muestra la línea “Se instalaría” en la ficha del paquete.
installed_source
Antes del motor fiable, “instalada” era siempre lo que resolvía el runner de CI (un entorno limpio, sin relación con lo que corre en PROD). Ahora el colector intenta primero GET /api/installed-packages en PROD ([[entity—api—endpoint—installed-packages]]); si no responde, cae a runner; si tampoco, a lockfile.
hold_reason y el resto de hold_*
Una fila con hold_reason no nulo está retenida a propósito: alguien decidió no subirla y dejó escrito el motivo. scripts/deps_holds.py (CreaRack-Pro) cruza el catálogo deps_holds.json contra las filas del colector y rellena estos cuatro campos antes del POST. El desfase (lag_kind) se sigue calculando igual, pero la fila no suma en los contadores major/minor del resumen ni en la cifra roja de la tarjeta de /tools — cuenta aparte como «retenida a propósito». DepsBadges.tsx la pinta en ámbar en vez de rojo/naranja.
Consumidores
GET /api/tools/deps([[entity—functions—endpoint—tools-deps]]): lee, ordena (avisos de seguridad primero, luegolag_kind), devuelve a la UI.DepsPanel.tsx/DepsBadges.tsx: filtran, buscan, renderizan tabla con badges (fuente PROD/CI/lock, “Sin techo”, “basta redeploy”/“cambiar pin”, “en N manifiestos”, “Retenida a propósito”).
Ciclo de vida
- Creación: migración
0042(2026-05-28). - Ampliaciones:
0048(2026-07-11, seguridad OSV),0052(2026-08-28, motor fiable) y0053(2026-08-28, retenciones) — todasALTER TABLE ADD COLUMN, sin migrar filas existentes (payloads viejos entran con las columnas nuevas aNULL/0). - Actualización:
POST /api/tools/deps(semanal vía GitHub Actions, o a mano).DELETE→INSERTbatch, snapshot atómico sin histórico. - Lectura:
GET /api/tools/deps(navegador, miles de veces).
Tamaño estimado
- Esperado: ~50–150 dependencias (entre ambos repos).
- Crecimiento: cero (snapshot, no histórico).
Véase también
- [[entity—functions—endpoint—tools-deps]]
- [[feature—workspace—tool-dependencias]]
- [[runbook—workspace-tech—tool-dependencias]]
- [[entity—api—endpoint—installed-packages]]
- [[feature—harness—osv-security-deps-freshness]]