ontoref/assets/presentation/ontology_slides.md
Jesús Pérez 82a358f18d
Some checks failed
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (push) Has been cancelled
feat: #[onto_mcp_tool] catalog, OCI credential vault layer, validate ADR-018 mode hierarchy
ontoref-derive: #[onto_mcp_tool] attribute macro registers MCP tool unit-structs in
  the catalog at link time via inventory::submit!; annotated item is emitted unchanged,
  ToolBase/AsyncTool impls stay on the struct. All 34 tools migrated from manual wiring
  (net +5: ontoref_list_projects, ontoref_search, ontoref_describe,
  ontoref_list_ontology_extensions, ontoref_get_ontology_extension).

  validate modes (ADR-018): reads level_hierarchy from workflow.ncl and checks every
  .ncl mode for level declared, strategy declared, delegate chain coherent, compose
  extends valid. mode resolve <id> shows which hierarchy level handles a mode and why.
  --self-test generates synthetic fixtures in a temp dir for CI smoke-testing.

  validate run-cargo: two-step Cargo.toml resolution — workspace layout first
  (crates/<check.crate>/Cargo.toml), single-crate fallback by package name or repo
  basename. Lets the same ADR constraint shape apply to workspace and single-crate repos.

  ontology/schemas/manifest.ncl: registry_topology_type contract — multi-registry
  coordination, push targets, participant scopes, per-namespace capability.

  reflection/requirements/base.ncl: oras ≥1.2.0, cosign ≥2.0.0, sops ≥3.9.0, age
  ≥1.1.0, restic declared as Hard/Soft requirements with version_min, check_cmd, and
  install_hint (ADR-017 toolchain surface).

  ADR-019: per-file recipient routing for tenant isolation without multi-vault. Schema
  additions: sops.recipient_groups + sops.recipient_rules in ontoref-project.ncl.
  secrets-bootstrap generates .sops.yaml from project.ncl in declarative mode. Three
  new secrets-audit checks: recipient-routing-coherent, recipient-routing-coverage,
  no-multi-vault. Adoption templates: single-team/, multi-tenant/, agent-first/.
  Integration templates: domain-producer/, mode-producer/, mode-consumer/.

  UI: project_picker surfaces registry badge (⟳ participant) and vault badge
  (⛁ vault_id · N, green=declarative / amber=legacy) per project card. Expanded panel
  adds collapsible Registry section with namespace, endpoint, and push/pull capability.
  manage.html gains Runtime Services card — MCP and GraphQL toggleable without restart
  via HTMX POST /ui/manage/services/{service}/toggle.

  describe.nu: capabilities JSON includes registry_topology and vault_state per project.
  sync.nu: drift check extended to detect //! absence on newly registered crates.
  qa.ncl: six entries — credential-vault-best-practice (layered data-flow diagram),
  credential-vault-templates (paths A/B/C), credential-vault-troubleshooting (15 named
  errors), integration-what-and-why (ADR-042 OCI federation), integration-how-to-implement,
  integration-troubleshooting.

  on+re: core.ncl + manifest.ncl updated to reflect OCI, MCP, and mode-hierarchy nodes.
  Deleted stale presentation assets (2026-02 slides + voice notes).
2026-05-12 04:46:15 +01:00

33 KiB

theme title titleTemplate layout keywords download exportFilename monaco remoteAssets selectable record colorSchema lineNumbers themeConfig fonts drawings scripts class
default Ontology and Reflection %s - OntoRef cover Nickel,Nushell,Ontology,Reflection,StratumIOps true OntoRef true true true true dark false
primary logoHeader
#f74c00 /ferris.svg
mono
Victor Mono
enabled persist presenterOnly syncAll
true false false true
setup/image-overlay.ts
justify-center flex flex-cols

Ontology & Reflection

Typing the invisible. Verifying the obvious. Sealing what matters.

layout: two-cols

¿Qué estamos resolviendo?

Los problemas

🔴
Epistemic drift
El código hace X, el equipo cree que hace Y.
Silencioso. Acumulativo.
🔴
Multi-actor collision
Humano + agente IA escriben al mismo fichero,
sin coordinación.
🟠
Decision amnesia
¿Por qué se tomó esta decisión? ¿Quién la autorizó?
🟠
Config schizophrenia
Staging funciona, prod no. ¿Qué campo cambió y cuándo?

::right::

🟠
Docs como artefactos muertos
Escritas una vez, olvidadas, nunca actualizadas.
🟡
Agent con ceguera
Los agentes IA empiezan sin contexto,
violan invariantes establecidas.

Ejemplo: desincronización real

# Documentación (2024-01)
config.max_retries = 3

# Código actual (después de cambio sin registrar)
config.max_retries = 7

# Equipo cree que está usando 3 ✗
# Sistema usa 7 silenciosamente ✓
# Nadie sabe cuándo cambió ni por qué

layout: default

El coste de ignorarlo

Deuda técnica      → arreglable
Deuda epistémica   → sistémica
Deuda de confianza → letal
Técnica
Refactoriza, reescribe, corrige.
1-3 sprints
Epistémica
Acumula silenciosamente. No sabes dónde.
Crece cada semana
Confianza
Se disuelve cuando se descubre el drift.
Imposible reparar rápido

Escenario: 6 meses de ignorar

Mes 1: "Solo refactorizar esto"
Mes 2: "Documentación está desactualizada"
Mes 3: "¿Por qué prod falla y staging no?"
Mes 4: "Nadie sabe quién cambió qué"
Mes 5: Pánico. Rollback arriesgado.
Mes 6: Equipo rechaza cambios nuevos.
         "No confiamos sin entender"
Estos problemas no tienen solución técnica.
Tienen solución de arquitectura.

layout: default

Verdad y operación — dos fuerzas que deben coexistir

Yin — la capa formal

Lo que debe ser verdad

  • Nickel schemas → corrección estructural
    en tiempo de definición
  • ADR constraints → "esto nunca puede violarse"
  • Config seals → estados sellados, verificables con sha256
  • Ontology invariants → lo que no puede cambiar
    sin un nuevo ADR
  • Hashes matemáticos → prueba de qué fue sellado
    y cuándo

Yang — la capa operacional

Cómo las cosas se mueven y cambian

  • Nu commands → transformación estructurada de datos
  • Actors (human/agent/CI) → mismo protocolo,
    distintas capacidades
  • Register flow → captura cambios,
    los enruta al artefacto correcto
  • Mode definitions → secuencias de operaciones
    con verificación
  • Pre-commit hooks → sincronización forzada
    en el momento del commit
Yang sin Yin  = fluido pero caótico.  Cualquier cosa puede cambiar.       Nada es verificable.
Yin  sin Yang = correcto pero inútil. Schemas perfectos sin operaciones = documentación muerta.
El sistema vive en la coexistencia.

layout: center

Los ingredientes

CAPA DECLARATIVA  ·  Nickel
.ontology/  ·  adrs/  ·  reflection/schemas/  ·  reflection/configs/
Tipos fuertes, contratos, enums. Falla en definición, no en runtime.
CAPA OPERACIONAL  ·  Nushell
adr  ·  register  ·  config  ·  backlog  ·  forms  ·  prereqs
Pipelines tipadas sobre datos estructurados. No streams de texto.
PUNTO DE ENTRADA  ·  Bash → Nu
stratum.sh  ·  actor detection  ·  locking  ·  NICKEL_IMPORT_PATH
Un único entry point. Detecta actor, adquiere lock, despacha al módulo Nu correcto.
GRAFO DE CONOCIMIENTO  ·  Ontología + ADRs
nodes  ·  invariants  ·  gates  ·  dimensions  ·  states
El sistema sabe qué sabe. Actor-agnostic. Machine-queryable.
ESTADOS SELLADOS  ·  Config + Historia
profiles  ·  sha256 seals  ·  audit trail  ·  rollback
Inmutabilidad verificable. Drift detection. Trazabilidad completa ADR/PR/bug.

layout: default

Nickel — configuración con tipos, sin runtime

Qué es Nickel

  • Lenguaje de configuración con tipos fuertes (de Tweag)
  • Puramente declarativo: sin efectos secundarios, sin loops, sin runtime
  • Superset de JSON: JSON válido es Nickel válido
  • Imports: let s = import "schema.ncl" in
  • Contratos: field | TypeName — verificado al merge
  • Enums tipados: [| 'Proposed, 'Accepted, 'Superseded |]
  • Registros abiertos/cerrados:
    { _: String } vs { field | String }
  • nickel export file.ncl → JSON estable pipelines Nu
El sistema de tipos es la documentación.

Por qué no alternativas

YAML  → sin tipos, whitespace-sensitive,
        coerciones implícitas
TOML  → sin tipos ≥ primitivos, sin imports
JSON  → sin comentarios, sin imports, sin tipos
HCL   → efectos secundarios, no es config pura
CUE   → similar pero peor historia de imports
KCL   → tipos + validación, pero vendedor
        lenguaje opaco
Por qué aquí específicamente
  • ADR constraints necesitan typecheck, no validación manual
  • Perfiles de config deben fallar en definición, no en runtime
  • nickel export produce JSON estable para consumo Nu
# YAML: silently accepts garbage
actor: developr   # typo, accepted
# Nickel: rejected at typecheck
{ actor | [| 'developer, 'agent, 'ci |] = "developr" }
#                       ^^^^^^^^^
#                error: tag not in enum type

layout: default

Nushell — datos estructurados, no streams de texto

Shell tradicional: texto opaco

# Bash: parsear texto, error-prone
grep 'severity' adrs/adr-001.json |\
    python3 -c "import sys,json; ..."
Nushell: records nativos
nickel export adrs/adr-001.ncl
  | from json
  | get constraints
  | where severity == "Hard"
  | select id claim
╭───┬────────────┬──────────────────────────────╮
│ # │ id         │ claim                        │
├───┼────────────┼──────────────────────────────┤
│ 0 │ adr-001-c1 │ nickel export must succeed…  │
│ 1 │ adr-001-c2 │ schema is sole source of…    │
╰───┴────────────┴──────────────────────────────╯

Nickel → Nu → Bash — tres capas, tres roles

Nickel exporta JSON tipado
Nunca strings mágicos. Contratos en la definición, no en runtime.
Nu consume estructuras
Tables, records, pipelines. Filter, sort, merge — sin awk ni sed.
Bash orquesta procesos
Locking, signals, exec. Solo lo que bash hace de forma nativa.
# Constraints Hard del sistema completo
glob "adrs/adr-*.ncl"
| each { |f| nickel export $f | complete | get stdout }
| each { from json }
| where status == "Accepted"
| get constraints | flatten
| where severity == "Hard"

layout: default

DAGs y FSMs — grafos acíclicos y máquinas de estado

FSM: ciclo de vida de un ADR

  ┌──────────┐  adr accept  ┌──────────┐
  │ Proposed │ ───────────▶ │ Accepted │
  └──────────┘              └──────────┘
                                  │ new ADR
                                  │ supersedes
                                  ▼
                            ┌────────────┐
                            │ Superseded │
                            └────────────┘
Transiciones typecheck-enforced en Nickel:
status | [| 'Proposed, 'Accepted, 'Superseded |]
superseded_by | String | default = ""
Las transiciones inválidas no existen
— el tipo las elimina del espacio de posibilidades.
Un ADR no puede volver a Proposed.
Superseded no puede tener constraints activas.

DAG: ontología como grafo de dependencias

invariante: backend-agnostic-core
  └─ tensión: nickel-as-canonical-schema
       └─ gate: nickel-primacy-gate [activa]
            └─ protege: nickel-integration-depth

invariante: zero-external-runtime-core
  └─ gate: core-dependency-gate [activa]
       └─ protege: backend-agnostic-core
Por qué importa la aciclicidad
→ Un invariante no puede depender de sí mismo
→ Las gates protegen nodos hoja, no ciclos
→ El rollback es determinista: sin ambigüedad de orden
# Invariantes activos
nickel export .ontology/core.ncl
| from json | get nodos
| where invariante == true
| select id descripcion

layout: default

La Ontología Operacional — el grafo operacional

core.ncl — lo que no puede cambiar

# .ontology/core.ncl
{
  nodos = [
    {
      id          = "backend-agnostic-core",
      invariante  = true,
      descripcion = "business logic sin deps de UI",
      tensiones   = ["nickel-as-canonical-schema"],
    },
  ],
  practicas = [...],
}
Tocar un nodo con invariante = true requiere un nuevo ADR. Sin excepción.

state.ncl y gate.ncl — dónde estamos

nickel export .ontology/state.ncl
| from json | get dimensiones
| select id estado_actual estado_deseado
backend-maturity         multi-stable  → all-production
nickel-integration-depth schema-input  → bidirectional
nickel export .ontology/gate.ncl
| from json | get membranas
| where activa == true
| select id permeabilidad protege
core-dependency-gate  Low  [backend-agnostic-core, ...]
nickel-primacy-gate   Low  [nickel-as-canonical-schema]
La ontología no es un diagrama.
Es un grafo consultable que el sistema y los agentes leen.

layout: default

Reflection — modos, formas, módulos, perfiles

Modo: especificación ejecutable

# reflection/modes/new_service.ncl
{
  id      = "new_service",
  trigger = "When adding a service",
  steps   = [
    {
      id       = "create-adr",
      actor    = "developer",
      action   = "Draft ADR for service boundary",
      cmd      = "stratum form new_adr",
      on_error = { strategy = "abort" },
    },
    {
      id         = "seal-config",
      actor      = "developer",
      action     = "Apply initial config seal",
      cmd        = "stratum config apply development",
      depends_on = [{ step = "create-adr" }],
      on_error   = { strategy = "abort" },
    },
  ],
}

Forma: entrada estructurada tipada

# reflection/forms/register_change.ncl
{
  elements = [
    { type = "section_header", label = "Change" },
    {
      type     = "text",
      id       = "title",
      label    = "Title",
      required = true,
    },
    {
      type    = "select",
      id      = "change_type",
      options = ["feature","fix","refactor","config"],
    },
  ]
}
Perfil de config = estado sellado
sha256(nickel export profile.ncl) — drift detection instantánea
stratum config verify development → compara hash actual con hash sellado

layout: default

ADRs vivos

— de documento muerto a constraint machine

Estructura Nickel de un ADR

# adrs/adr-001-nickel-as-canonical.ncl
let s = import "adrs/schema.ncl" in
{
  id      = "adr-001",
  title   = "Nickel as First-Class Form Definition",
  status  = "Accepted",
  date    = "2025-12-01",
  constraints = [
    {
      id       = "adr-001-c1",
      severity = "Hard",
      claim    =
        "nickel export must succeed before "
         ++ "any user interaction",
    },
  ],
  superseded_by = "",
} | s.ADR

Ciclo de vida y consulta

Proposed ──────▶ Accepted ──────▶ Superseded
  │                                   ▲
  │            new ADR                │
  └──────── superseded_by ────────────┘
Constraints Hard activas del sistema
glob "adrs/adr-*.ncl"
| each { |f|
    nickel export $f | complete
    | if $in.exit_code == 0 { 
        $in.stdout | from json 
    } else { null }
  }
| compact
| where status == "Accepted"
| get constraints | flatten
| where severity == "Hard"
| select id claim
El conjunto activo de constraints es el contrato en vigor — no la prosa del ADR. La prosa explica el porqué. Los constraints enforcan el qué.

layout: default

Implementación — las partes y cómo encajan

Stack de archivos operativos

stratum.sh                 ← entry point, locking, env
  └─ reflection/bin/stratum.nu  ← dispatcher
       ├─ modules/adr.nu        ← ADR lifecycle
       ├─ modules/register.nu   ← CHANGELOG + ontology
       ├─ modules/config.nu     ← sealed profiles
       ├─ modules/backlog.nu    ← backlog items
       └─ modules/forms.nu      ← TypeDialog integrated

.ontology/                 ← queryable truth
  ├─ core.ncl              ← invariants
  ├─ state.ncl             ← dimensions
  └─ gate.ncl              ← active guards

adrs/                      ← typed decisions
reflection/configs/        ← sealed config history

Flujo de escritura protegida

stratum.sh
detect actor → acquire lock (mkdir) → delegate → release
Lock resources
manifest (config apply/rollback) · changelog (register) · backlog (done/cancel)
Timestamp IDs
cfg-20260309T045206-developer — colisión imposible, sin TOCTOU
# Advisory lock: mkdir es POSIX-atómico
mkdir .stratumiops/locks/manifest.lock 2>/dev/null
echo "$$:developer:$(date -u +%Y%m%dT%H%M%SZ)" 
    > .stratumiops/locks/manifest.lock/owner

layout: default

Mecánica y flujo — casos de uso reales

Flujo: registrar un cambio

stratum register → forma TypeDialog interactiva
Clasificar: tipo de cambio, impacto, ADR relacionado
Artefacto: entrada CHANGELOG + stub ADR si aplica
Verificar: nickel typecheck debe pasar
Sellar si afecta config: stratum config apply
Detección de drift
stratum config apply production --adr adr-006 --pr 42
# Meses después:
stratum config verify production
# → ✓ verified  a3f7c12d8b4e9f01...
# → ✗ DRIFT DETECTED stored vs current hash

Rollback verificado

stratum config history production

stratum config rollback production \
    cfg-20260301T120000-developer  \
    --adr adr-007 \
    --note "revert breaking change"
snapshot_hash verifica integridad antes de restaurar
→ Rollback genera nuevo sello — la historia es append-only
→ Trazabilidad completa: ADR + PR + bug en cada sello
cfg-20260301T120000-developer  (original)
  └─ cfg-20260309T045206-agent  (change)
       └─ cfg-20260309T120000-developer  (rollback)

layout: default

Integración en proyectos — onboarding al ecosistema

Lo que un proyecto necesita

proyecto/
  .ontology/
    core.ncl       ← invariants del proyecto
    state.ncl      ← dimensiones de madurez
    gate.ncl       ← guards activos
  adrs/
    schema.ncl
    reflection.ncl
    adr-001-*.ncl  ← ADR fundacional
  reflection/
    forms/         ← TypeDialog forms
    modes/         ← operational modes
    configs/       ← sealed profiles
    modules/       ← Nu modules
    bin/stratum.nu
  stratum.sh

Inicialización

Comando de onboarding
/onboard-project → genera .ontology/
+ ADR fundacional
Primer ADR obligatorio
Define las invariantes — qué no puede cambiar sin ADR nuevo
Pre-commit como sincronización
nickel typecheck en cada commit
— la capa declarativa nunca queda rota
./stratum.sh check
# ✓ nushell 0.110.0
# ✓ nickel 1.9.0
# ✓ .ontology/core.ncl exportable
# ✓ adrs/adr-001 Accepted
# ✗ no sealed config profiles yet

layout: default

Integración con Claude Code & Uso concurrente

SessionStart: contexto automático

stratum-session-start.sh se ejecuta al inicio de cada sesión:
=== STRATUM CONTEXT: typedialog ===

ADRS
  adr-001 [Accepted] Nickel as First-Class Form...
  adr-002 [Accepted] NCL element order from position

HARD CONSTRAINTS
  adr-001-c1  nickel export must succeed before...
  adr-002-c1  web/tui must render headers interleaved

STATE
  backend-maturity: multi-stable → all-production

ACTIVE GATES
  nickel-primacy-gate [Low] Protects: nickel-as-...

=== END STRATUM CONTEXT ===

Uso concurrente: humano + agente IA

IDs timestamp con actor
cfg-20260309T045206-developer
cfg-20260309T045207-agent
Sin coordinador. Sin TOCTOU. Trazabilidad por actor.
Advisory locks (mkdir)
Serializa escrituras en manifest/changelog/backlog. Stale detection por PID.
Actor-agnostic protocol
El sistema no distingue humano de agente. El protocolo enforcea igual para ambos.
Worktrees aíslan el working-tree (branches diferentes).
Locking serializa escrituras sobre el mismo árbol.
Son complementarios, no alternativos.

layout: cover name: end class: 'justify-center flex flex-cols'

Ontology & Reflection

✓ Typed ✓ Verified ✓ Sealed ✓ Decided
El código y el entendimiento del código deben converger.
Los ADRs y la ontología son el contrato entre ambos.
La reflection es la operación sobre ese contrato.
Gracias. ¿Preguntas?

jesusperez.pro · ontoref.dev