33 KiB
theme, title, titleTemplate, layout, keywords, download, exportFilename, monaco, remoteAssets, selectable, record, colorSchema, lineNumbers, themeConfig, fonts, drawings, scripts, class
| 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 |
|
|
|
|
justify-center flex flex-cols |
Ontology & Reflection
layout: two-cols
¿Qué estamos resolviendo?
Los problemas
El código hace X, el equipo cree que hace Y.
Silencioso. Acumulativo.
Humano + agente IA escriben al mismo fichero,
sin coordinación.
¿Por qué se tomó esta decisión? ¿Quién la autorizó?
Staging funciona, prod no. ¿Qué campo cambió y cuándo?
::right::
Escritas una vez, olvidadas, nunca actualizadas.
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
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"
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.
layout: center
Los ingredientes
.ontology/ · adrs/ · reflection/schemas/ · reflection/configs/adr · register · config · backlog · forms · prereqsstratum.sh · actor detection · locking · NICKEL_IMPORT_PATHlayout: 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
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
- ADR constraints necesitan typecheck, no validación manual
- Perfiles de config deben fallar en definición, no en runtime
nickel exportproduce 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; ..."
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
# 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 │
└────────────┘
status | [| 'Proposed, 'Accepted, 'Superseded |]
superseded_by | String | default = ""
— 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
# 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 = [...],
}
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]
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"],
},
]
}
sha256(nickel export profile.ncl) — drift detection instantáneastratum 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 ────────────┘
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
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
manifest (config apply/rollback) · changelog (register) · backlog (done/cancel)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 interactivanickel typecheck debe pasarstratum config applystratum 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 restaurarcfg-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
/onboard-project → genera .ontology/
+ ADR fundacionalnickel 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
cfg-20260309T045207-agent
Locking serializa escrituras sobre el mismo árbol.
Son complementarios, no alternativos.
layout: cover name: end class: 'justify-center flex flex-cols'
Ontology & Reflection
Los ADRs y la ontología son el contrato entre ambos.
La reflection es la operación sobre ese contrato.
jesusperez.pro · ontoref.dev