Skip to content

Releases: syberloop/mcp-okf

v0.4.32 — el agente ve la vigente, el costo y el cuerpo

Choose a tag to compare

@Jabar42 Jabar42 released this 01 Nov 17:52

Seis PRs de Medardo (#41–#46), verificados uno por uno y en conjunto. Suite: 583 passed, 57 subtests.

Búsqueda y vigencia (#43, #46)

  • search --body busca también en el cuerpo; los de frontmatter van primero y los de cuerpo después, marcados (cuerpo). Sin --body y con menos de 5 resultados, un pie avisa cuántas notas más lo mencionan en el cuerpo.
  • read de una nota reemplazada imprime, antes del cuerpo, la versión vigente; la correctora imprime a quién corrige.
  • El pie de reemplazadas de search deja de ser el total del vault: cuenta solo las que coinciden con la consulta y el tipo, cada una con su vigente.
  • Conflicto #43/#46 resuelto: los dos reescribían el pie de search; conviven los dos y hidden se filtra con matches_query(..., body=body_mode).

Costo de la llamada (#45)

  • traverse --compact (una línea por nodo) y --budget N (corta después del BFS, «alcanzables» exacto).
  • Recibo en toda salida: nodos, alcanzables, bytes, tokens y si truncó. receipt en JSON.

Grafo (#41, #42)

  • suggest-edge-types no propone como ALTA la inversa de una arista tipada ni los pares mutuos; no entran en --apply. En un vault de 311 notas: ALTA 391 → 271.
  • graph impact manda por la arista declarada; el score semántico solo anota ([ajuste: 0.65]) y avisa si es bajo.

Validación (#44)

  • aplica hacia una reemplazada avisa «re-anclá la arista… el trabajo puede seguir valiendo».
  • Apoyo en una nota muerta sin sucesora (estados dead de su capa) avisa «quedó sin base». Aviso, nunca error.

v0.4.31 — sello de identidad de vault

Choose a tag to compare

@Jabar42 Jabar42 released this 23 Oct 22:01

[2026-10-23] — v0.4.31

Con tres vaults servidos por el mismo server.py, la única falla silenciosa posible era elegir el prefijo equivocado (mcp__okf__new en vez de mcp__okf_<cliente>__new): el docstring de las tools era idéntico en las tres instancias y la respuesta no decía sobre qué vault se había escrito. Un new mal dirigido creaba un concepto huérfano en el grafo equivocado, con la taxonomía del padre.

Sello de identidad de vault (server.py)

  • El schema declara el vault (_vault_label() + _mcp_tool()): el docstring de cada tool se sella con Vault de esta instancia: <nombre> (<ruta absoluta>) antes de registrarla. El agente lo lee en el schema ANTES de llamar, así el prefijo equivocado deja de ser invisible. El sello se re-aplica sin acumularse en reimportaciones.
  • La escritura se verifica E2E (_seal_output()): toda tool de _WRITE_TOOLS cierra su salida con [vault: <nombre> (<ruta>)]; si el CLI cita un .md absoluto fuera de este vault → [ERROR: la salida cita archivos fuera de este vault: …]; si lo cita como creado y no existe → [ERROR: el CLI reporta éxito pero no existe: …]. Cubre también los caminos de timeout y excepción.
  • --dry-run no se verifica: no escribe, así que exigir la existencia del archivo era un falso positivo. Detectado con un mini-cliente stdio real sobre los tres servers antes de cortar la etiqueta.
  • Tests: tests/test_multivault_seal.py (+12): label con nombre y ruta, sello en escritura y no en lectura, archivo fuera del vault, éxito fantasma, ruta relativa que no debe confundirse con absoluta (insights/x.md ≠ /x.md), exit≠0 sellado sin verificar, edit sellado, dry-run sin verificación, y el docstring sellado en subproceso (borrar cli.* de sys.modules para recargar contamina los monkeypatches de otros tests: rompía test_scope_readonly y test_stale_reads_store). Suite: 547.
  • Medido en los tres vaults (2026-10-23): okf (721 nodos, 9/9), okf-skynet (306, 8/8), okf-controlresearch (79, 8/8) — cada server se identifica con su propio vault, sin filtrar el nombre de otro, y una escritura real por stdio devuelve el sello con el path absoluto correcto.

v0.4.30 — el review omite los conceptos muertos (states.dead)

Choose a tag to compare

@Jabar42 Jabar42 released this 22 Oct 15:55

Full Changelog: v0.4.29...v0.4.30

v0.4.29 — fases 4 y 5: version vigente, verificacion, Persona e iniciativa con scope

Choose a tag to compare

@Jabar42 Jabar42 released this 21 Oct 16:06

Las dos fases del rediseño del vault de cliente, juntas en una etiqueta. Todo opt-in por vault: sin las claves nuevas en .okf.config.yaml el comportamiento es el de v0.4.28.

Fase 4 (#39) — la verdad tiene fecha, quien decide es una nota y una nota reemplazada señala su vigente:

  • resolves: <Tipo> en types.fields: decidida_por y responsable resuelven a una nota Persona en vez de a texto libre.
  • verificado_el / revisar_el con plazo por tipo (types.review.days) o por prefijo de ruta (paths, que gana); stale suma la señal 8 y review separa kind: cyber de kind: verificacion.
  • traverse y search saltan a la versión vigente por defecto (--all-versions recorre la cadena).
  • health deja de dar 7/8 en un vault cuyos tipos exigen campos.

Fase 5 (#40) — la iniciativa como campo y el alcance por campo:

  • types.fields.common y campos con lista, para exigir iniciativa en toda nota salvo la capa de registro y las tareas.
  • --scope campo=valor: la frontera de una instancia MCP puede ser un campo del frontmatter. Fail-closed, y un scope mal formado aborta en vez de degradar.

Tests: 523 (485 en v0.4.28), verificados también en un entorno limpio tipo CI. Verificación: suite en worktree y entorno limpio, baseline RED, mediciones reproducidas sobre un clon fresco del vault de cliente, y el efecto sobre OKF-Vault (6 notas ocultas en search, 2 avisos nuevos, health 9/9).

v0.4.28 — el tablero lee las aristas de cada tarjeta

Choose a tag to compare

@Jabar42 Jabar42 released this 19 Oct 18:55

El board deja de exportar tarjetas mudas: cada una trae sus aristas tipadas salientes (relaciones: tipo, destino y título del destino) y cuántas le entran (entrantes), para que el tablero diga de qué plan es parte una tarea o qué espera una nota bloqueada sin abrirla. Es el PR #38, que había quedado sin etiqueta.

Medido (2026-10-17, master del vault de cliente): 268 tarjetas, 218 con al menos una relación, 360 aristas, 0 destinos sin título; las 37 Task traen su aplica.

Tests: 485 (484 en v0.4.27).

Requiere subir OKF_VERSION a v0.4.28 en las Actions de los vaults que consumen okf board.

v0.4.26 — okf board, y el detector de obsolescencia con una sola vara

Choose a tag to compare

@Jabar42 Jabar42 released this 16 Oct 14:56

okf board (PR #32) y el detector de obsolescencia con una sola vara.

Added

  • okf board [--out] [--iniciativa X] [--no-tasks]: el vault como JSON de tablero en una sola pasada — columnas (los estados de cada capa según types.states), tarjetas (un concepto tipado por tarjeta, fuera de la capa de registro, con capa, status, iniciativa, casillas hechas/total, edad y nivel de stale), tareas (casillas abiertas con antigüedad, reutiliza search.find_todos) y salud (conteo por capa y estado). --iniciativa filtra por el campo; sin esquema de estados sigue funcionando con una capa sin-esquema y los status observados.
  • PR #32, Medardo Rafael Hernandez Franco (7 tests).

Fixed

  • dashboard_snapshot no le pasaba config a collect_stale: el detector caía en los literales legacy (señal 3 = propuesta, señal 6 = Decision) mientras stale y board leían el esquema del vault. Medido en OKF-Vault con el esquema activo: 72 de 700 conceptos divergían — el panel del DashboardView daba ATTENTION donde el CLI daba STALE—. Misma clase que el #21, el #29 y el #30: dos consumidores de la misma primitiva midiendo distinto.
  • collect_stale(vault, config=cfg) ignoraba la lista de exentos del vault (types.exempt_from_no_backlinks) y usaba la embebida en silencio: el resultado dependía de cómo se llamaba a la función y no de la config. Ahora, con config, manda la lista del vault; los llamadores que la pasan explícita no cambian.

Medido (2026-10-16, sobre OKF-Vault con los dos strict activos)

  • board: 565 tarjetas, 271 tareas, cuatro capas (conocimiento 4 estados, compromiso 4, trabajo 5, infra 3).
  • El desajuste salud.por_estado vs columnas no puede ocurrir con types.states.strict: true: toda tarjeta tiene un estado declarado por su capa (0 casos, 0 tarjetas sin status).
  • stale y el panel: 0 divergencias entre 700 conceptos (eran 72). STALE 6, ATTENTION 291.

Tests

  • 451 (443 en v0.4.25): +7 del board, +1 de paridad panel/CLI.

v0.4.25 — El destino de una arista se resuelve como el resto del CLI

Choose a tag to compare

@Jabar42 Jabar42 released this 16 Oct 14:47

El destino de una arista se resuelve como el resto del CLI (PR #33).

Fixed

  • validate resolvía el destino de una arista con vault / target a secas, sin el fallback de .md, mientras el resto del CLI acepta targets sin extensión (resolve_link). Un link target: decisions/otra-nota —como los escribe el vault OKF— no resolvía: el tipo del destino quedaba en '?' y el par era imposible de declarar en valid_pairs, así que graph.strict_pairs: true era inalcanzable en un vault que escribe targets sin extensión. Medido en OKF-Vault: 6 de 148 aristas atípicas estaban en ese estado (Harness aplica ?, Plan aplica ?, Project aplica ?, Harness depende ?, Skill depende ?) y ninguna se podía declarar.
  • La misma resolución estaba mal en la rama de corrige: no leía el destino, así que no comprobaba que estuviera en su estado de reemplazo. Con un target sin extensión, la comprobación no corría.

Changed

  • Las dos ramas usan _resolve_edge_target, que reusa resolve_link: la MISMA resolución que graph y que el chequeo de wikilinks (misma clase que el #29 y el #30 — una sola resolución por concepto), con fallback al path pelado y a .md para targets fuera de alcance.

Tests

  • tests/test_validate_edge_target_sin_extension.py (+4): par declarado con target sin .md no avisa; par atípico con target sin .md reporta el tipo real (no '?'); con strict_pairs es un error; un target inexistente sigue siendo '?'.
  • Suite: 443 (439 en v0.4.24).

Verificado de punta a punta: con este fix, el vault OKF declaró sus 143 pares y activó graph.strict_pairs: true + types.states.strict: true con validate --all en 701/701, 0 avisos.

v0.4.24 — La máquina de estados vive en la config del vault

Choose a tag to compare

@Jabar42 Jabar42 released this 16 Oct 13:13

Estado de los conceptos declarado en el vault (PR #31, Medardo Rafael Hernandez Franco).

Added

  • Máquina de estados por tipo, declarada en la config (types.states.schemas, agrupada por capa): types, states, initial, transitions, replaced (el estado que exige un corrige entrante) y pending (lo que stale cuenta como sin resolver). states: [] = capa de registro, no lleva status. types.states.strict (default false) decide aviso o error.
  • new sin --status arranca en el estado inicial del tipo; con --status, el valor tiene que estar en el esquema. Un tipo de registro no admite ninguno.
  • edit --status valida el estado y la transición desde el actual; --force salta la transición (migraciones).
  • graph.inherit_default_pairs: false deja fuera los valid_pairs embebidos —usan el vocabulario de un vault (LeccionAprendida, no Lesson)— para que cada vault declare los suyos con sus propios nombres de tipo, conservando descripción, transitividad e inversa.
  • graph.strict_pairs: true: un par (tipo origen, tipo destino) fuera de los declarados pasa de aviso a error en validate, y new/edit no escriben la arista.
  • okf stamp [--staged]: fija timestamp desde el diff de contenido contra HEAD; con --staged vuelve a stagear lo que ajustó, para el pre-commit.

Changed

  • timestamp mide el contenido, no los metadatos: edit lo renueva solo si cambian body, title o description. Aplica también sin esquema — es un cambio global, no una rama opt-in.
  • stale señal 3 lee pending del esquema del tipo; sin esquema, el literal histórico propuesta. Señal 6 exige status a cualquier tipo con estados declarados; sin esquema, a Decision.
  • La regla de corrige mira el estado replaced de la capa del destino; sin esquema conserva la heurística histórica (deprec/superced).
  • La validación 12 de validate (status contra el esquema) se suma a las existentes; sin esquema no comprueba nada.

Medido (2026-10-16)

  • Suite: 396 → 439 (43 nuevos). Revirtiendo cli/ a master, los nuevos dan 24 fallos + 3 errores: no son vacíos.
  • validate --all y stale --json sobre OKF-Vault (699 archivos, sin types.states): salida idéntica a v0.4.23.
  • El check Timestamp↔git de health no se rompe: solo alerta si el timestamp va en el futuro respecto al último commit o más de 30 días antes del primero.

Known follow-up

  • dashboard_snapshot._collect_stale_results llama a collect_stale(...) sin el nuevo config=: inerte sin esquema, pero con uno activo el panel DashboardView divergiría de stale y board. Repro en el comentario de revisión del PR #32.

Esta etiqueta es la que tienen que instalar los tres entornos (Action del vault, venv del Mac, Skynet) antes de activar el esquema de estados: es el paso que el plan de fases deja como condición del merge del PR #8 del vault.

v0.4.23

Choose a tag to compare

@Jabar42 Jabar42 released this 16 Oct 03:32

[2026-10-15] — v0.4.23

Changed

  • Los tipos exentos de la señal 4 de stale («no backlinks») salen de config: types.exempt_from_no_backlinks (con default embebido). La lista estaba hardcodeada con el vocabulario de un vault —("MarcoTeorico", "Spec", "Tool", "Agente", "Sesion")— mientras types.valid sí es configurable: en el vault del cliente, donde el tipo es Session y no Sesion, 9 sesiones entraban como señal 4 y el mismo número significaba cosas distintas en cada vault. El default embebido es el set del Criterio de obsolescencia más los dialectos conocidos: MarcoTeorico, Spec, Tool, Agente, Sesion, Session, Skill, Insight.
  • Señal 3: el umbral de status: propuesta para type: Insight sube a 90 días (stale.propuesta_days_insight, default 90) y el resto de los tipos mantiene 30. Es la decisión del triaje automático del subgrafo dreaming (2026-09-05): una hipótesis tiene más vida que una propuesta de acción, su ciclo es integrarse o archivarse, no «aplicarse». Cierra N1 y N2 del plan del ajuste del detector, en propuesta desde el 2026-09-05.
  • dashboard_snapshot comparte ahora la config completa del detector: antes omitía stale.reads_zero_min_days y no podía ver los exentos, así que su distribución podía contradecir a stale (misma clase que el #21 y el #29).

Fixed

  • Dos tests de test_stale_reads_store.py usaban type: Insight para fabricar el caso de 3 señales. Con Insight exento de la señal 4 ese camino ya no existe: el caso se prueba con un tipo no exento (Research). El contrato del store de lecturas no cambia.

Medido (2026-10-15, sobre los vaults reales)

Vault Señal 4 STALE ATTENTION FRESCO
OKF-Vault (698 conceptos) 110 → 67 9 → 2 357 → 317 332 → 379
skynet (238 conceptos) 43 → 26 0 → 0 62 → 44 176 → 194

Tests: 396 (11 nuevos; RED 5 fallos + 2 errores contra el código anterior), verdes también en entorno limpio tipo CI (env -i + venv scratch).


v0.4.22

Choose a tag to compare

@Jabar42 Jabar42 released this 16 Oct 03:02

[2026-10-15] — v0.4.22

Fixed

  • health buscaba el hook siempre en <vault>/.git/hooks/pre-commit (#30). Con core.hooksPath hooks —la activación que recomienda el CLAUDE.md de skynet-vault— o dentro de un worktree (donde .git es un archivo), un vault sano daba 7/8 con «pre-commit hook does not exist» aunque el hook existiera y corriera: como ese hook termina en health --strict, el commit quedaba abortado y no había forma de commitear con la configuración recomendada. Repro medido en un clon nuevo del vault: 7/8 sin el fix, 8/8 con él. _hooks_dir(vault) le pregunta a git con git rev-parse --git-path hooks; dashboard_snapshot hereda el arreglo porque importa la misma función. tests/test_health_git_hook_hookspath.py (+5).
  • trace --layers hooks seguía leyendo .git/hooks a mano (misma clase que el #30, cerrada en el mismo lote): en un vault con core.hooksPath respondía «(no results)» en silencio aunque el hook existiera — demostrado en vivo sobre skynet antes y después. Ahora usa _hooks_dir de health: un solo lugar decide dónde vive el hook. tests/test_trace_hooks_dir.py (+4, RED 3/4 contra el código anterior).
  • La señal 4 de stale («no backlinks») resolvía los wikilinks por nombre de archivo e ignoraba las aristas tipadas (#29), así que contradecía a graph. Medido sobre skynet (239 conceptos, 2026-10-15): 51 conceptos marcados antes, 34 después — 17 falsos positivos corregidos (33 %), 12 por aristas tipadas y 5 por basenames repetidos ([[projects/x/x]] caía en cualquier x.md y la ficha real quedaba huérfana). build_backlinks_index usa ahora build_graph, la misma resolución que graph. Costo: stale pasa de ~0.5 s a ~0.68 s en 239 conceptos, porque construye el grafo completo (aceptable a esta escala). tests/test_stale_backlinks.py (+3).

Changed

  • El check del hook de health incluye en el mensaje la ruta donde buscó («looked in …»): un 7/8 se diagnostica sin leer el código (sugerencia del autor del #30).

Tests: 385.

Fuera de alcance (verificado, no incluido)

  • La tupla de tipos «naturalmente huérfanos» de la señal 4 lista Sesion, pero el vault usa type: Session (15 conceptos, 9 sin backlinks): esos siguen contando como «no backlinks». Es la diferencia entre los 60 que reporta el CLI y los 51 del conteo sin sesiones. tests/test_dashboard_snapshot_conceptos.py:73 fija la premisa con el typo. Decisión de producto pendiente (¿una sesión sin backlinks es señal de staleness?), fuera de este release.