Documenta la nueva estructura apps/ para aplicaciones ejecutables, el sistema de proposals, executions/assertions, motor de evaluación SQL, bucle reactivo (EJECUTAR→EVALUAR→REACCIONAR→PROPONER) y CLI completo para fn ops assertion/execution y fn proposal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
27 KiB
fn-registry
Registry personal de codigo reutilizable con busqueda FTS. Diseñado para composicion funcional y agentes.
El sistema tiene dos bases de datos SQLite:
- registry.db — conocimiento estatico: que funciones y tipos existen, como se componen, y que mejoras se proponen. Vive en la raiz del repositorio. Regenerable con
fn index(excepto proposals). - operations.db — conocimiento dinamico por proyecto: que entities existen, como se relacionan, que ejecuciones se han hecho, y que calidad tienen los datos. Vive en cada proyecto que usa el registry.
Arquitectura: dos BDs, un bucle
┌─────────────────────────────────────────────────────────────────────┐
│ registry.db (central) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ functions │ │ types │ │proposals │ │
│ │ (120+) │ │ (31+) │ │(auto/man)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ busca funciones ↑ crea proposals │
└───────┼────────────────────────────┼────────────────────────────────┘
│ │
┌───────┼────────────────────────────┼────────────────────────────────┐
│ ↓ usa en relaciones │ reactive loop │
│ operations.db (por proyecto) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ entities │→ │relations │ │executions│→ │assertions │ │
│ │ │ │ │ │ │ │ → assertion_results│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │relation_inputs│ │types_snapshot│ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Bucle autonomo: EJECUTAR → EVALUAR → REACCIONAR → PROPONER
- Un pipeline se ejecuta → se registra en
executions - Las
assertionsactivas de la entity se evaluan automaticamente - Si critical falla → entity pasa a
corrupted+ se creaproposalen registry - Si warning falla → entity pasa a
stale - El humano revisa proposals y decide si implementar mejoras
Explorar el registry (USAR SIEMPRE)
La BD SQLite registry.db es tu mapa del repositorio. Antes de escribir codigo, SIEMPRE consultala para saber que existe, evitar duplicados y descubrir funciones reutilizables.
# Buscar funciones por texto libre (FTS5)
sqlite3 registry.db "SELECT id, kind, purity, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'slice') ORDER BY name;"
# Listar todas las funciones de un dominio
sqlite3 registry.db "SELECT id, purity, signature FROM functions WHERE domain = 'finance' ORDER BY name;"
# Listar solo puras de un dominio
sqlite3 registry.db "SELECT id, signature FROM functions WHERE domain = 'core' AND purity = 'pure' ORDER BY name;"
# Listar solo impuras
sqlite3 registry.db "SELECT id, domain, error_type FROM functions WHERE purity = 'impure' ORDER BY domain, name;"
# Buscar tipos por dominio
sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE domain = 'cybersecurity';"
# Ver que funciones usa un pipeline o funcion compuesta
sqlite3 registry.db "SELECT id, uses_functions, uses_types FROM functions WHERE uses_functions != '[]';"
# Ver funciones que dependen de un tipo concreto
sqlite3 registry.db "SELECT id FROM functions WHERE uses_types LIKE '%ohlcv_go_finance%';"
# Buscar por tag
sqlite3 registry.db "SELECT id, tags FROM functions WHERE tags LIKE '%generic%';"
# Contar entradas por dominio
sqlite3 registry.db "SELECT domain, COUNT(*) FROM functions GROUP BY domain;"
# Ver todo el schema
sqlite3 registry.db ".schema"
# Ver proposals pendientes
sqlite3 registry.db "SELECT id, kind, status, title FROM proposals WHERE status = 'pending';"
La BD usa WAL mode — puedes leerla mientras se escribe sin bloqueos.
Tablas: registry.db
functions (27 columnas)
Cada funcion registrada: su firma, purity, dependencias, y metadata.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | {name}_{lang}_{domain} |
| name | TEXT | snake_case |
| kind | TEXT | function / pipeline / component |
| lang | TEXT | go / python / typescript / sql |
| domain | TEXT | core / finance / datascience / cybersecurity / ... |
| version | TEXT | semver |
| purity | TEXT | pure / impure |
| signature | TEXT | firma completa |
| description | TEXT | que hace y cuando usarla |
| tags | JSON[] | etiquetas |
| uses_functions | JSON[] | IDs de funciones que invoca |
| uses_types | JSON[] | IDs de tipos que recibe |
| returns | JSON[] | IDs de tipos que devuelve (no tipos nativos) |
| returns_optional | INT | 0/1 |
| error_type | TEXT | ID del tipo de error (obligatorio si impure) |
| imports | JSON[] | dependencias externas |
| example | TEXT | codigo de ejemplo extraido del .md |
| tested | INT | 0/1 |
| tests | JSON[] | nombres de tests |
| test_file_path | TEXT | ruta al archivo de test |
| file_path | TEXT | ruta relativa al .go/.py/.tsx |
| props | JSON[] | solo components: PropDef[] |
| emits | JSON[] | solo components: eventos emitidos |
| has_state | INT? | solo components: nullable |
| framework | TEXT | solo components: react/vue/... |
| variant | JSON[] | solo components: variantes |
| created_at, updated_at | TEXT | RFC3339 |
types (13 columnas)
Tipos algebraicos: product (struct) y sum (interface/union).
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | {name}_{lang}_{domain} |
| name | TEXT | snake_case |
| lang | TEXT | lenguaje |
| domain | TEXT | dominio |
| version | TEXT | semver |
| algebraic | TEXT | product / sum |
| definition | TEXT | codigo fuente del tipo |
| description | TEXT | descripcion |
| tags | JSON[] | etiquetas |
| uses_types | JSON[] | IDs de tipos que compone (sin auto-ref) |
| file_path | TEXT | ruta relativa |
| created_at, updated_at | TEXT | RFC3339 |
proposals (11 columnas)
Mejoras propuestas al registry. Las crea el agente (reactive loop) o el humano.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | proposal_{timestamp} o manual |
| kind | TEXT | new_function / new_type / improve_function / improve_type / new_pipeline |
| target_id | TEXT | ID de la funcion/tipo a mejorar (obligatorio para improve_*) |
| title | TEXT | titulo corto |
| description | TEXT | detalle |
| evidence | JSON{} | datos que justifican la propuesta (assertion failures, metrics, etc) |
| status | TEXT | pending / approved / rejected / implemented |
| created_by | TEXT | quien creo (agente, humano, reactive_loop) |
| reviewed_by | TEXT | quien reviso |
| created_at, updated_at | TEXT | RFC3339 |
FTS5 (busqueda full-text)
functions_fts— indexa: id, name, description, tags, signature, domaintypes_fts— indexa: id, name, description, tags, domainproposals_fts— indexa: id, title, description, evidence
Sincronizados automaticamente via triggers (INSERT/UPDATE/DELETE).
Tablas: operations.db
entities (12 columnas)
Instancia concreta de un tipo del registry dentro de un proyecto.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | identificador unico en el proyecto |
| name | TEXT | nombre descriptivo |
| type_ref | TEXT | ID del tipo en registry (ej: ohlcv_go_finance) |
| status | TEXT | active / stale / corrupted / archived |
| description | TEXT | que representa esta entity |
| domain | TEXT | dominio |
| tags | JSON[] | etiquetas |
| source | TEXT | origen de los datos (obligatorio) |
| metadata | JSON{} | campos del tipo instanciados con valores reales |
| notes | TEXT | notas libres |
| created_at, updated_at | TEXT | RFC3339 |
relations (17 columnas)
Conexion/transformacion entre entities. Puede ser causal (via funcion) o semantica.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | identificador |
| name | TEXT | nombre descriptivo |
| from_entity | TEXT | entity origen |
| to_entity | TEXT | entity destino (obligatorio) |
| via | TEXT | ID de funcion del registry que transforma (si vacio = semantica) |
| description | TEXT | que hace esta relacion |
| purity | TEXT | pure / impure / `` |
| direction | TEXT | unidirectional / bidirectional / inverse |
| weight | REAL | 0.0-1.0, importancia |
| status | TEXT | designed / implemented / tested / running / deprecated |
| started_at, ended_at | TEXT | ciclo de vida |
| order | INT | para secuencias |
| tags | JSON[] | etiquetas |
| notes | TEXT | notas |
| created_at, updated_at | TEXT | RFC3339 |
relation_inputs (5 columnas)
Multi-input para relaciones N→1 (joins, merges, agregaciones).
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | |
| relation_id | TEXT FK | referencia a relations.id (CASCADE delete) |
| entity_id | TEXT FK | referencia a entities.id |
| role | TEXT | rol semantico del input (ej: "left", "right", "config") |
| order | INT | orden de procesamiento |
types_snapshot (7 columnas)
Copia inmutable de un tipo del registry en el momento de uso. Hace operations.db autonoma.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | mismo ID que en registry.types |
| version | TEXT | version capturada |
| lang | TEXT | lenguaje |
| algebraic | TEXT | product/sum |
| definition | TEXT | codigo fuente capturado |
| description | TEXT | descripcion capturada |
| snapped_at | TEXT | cuando se hizo el snapshot |
executions (12 columnas)
Cada ejecucion de un pipeline. Memoria de comportamiento del sistema.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | exec_{timestamp} o manual |
| pipeline_id | TEXT | ID de funcion del registry (ej: tick_to_ohlcv_go_finance) |
| relation_id | TEXT | relacion asociada (opcional) |
| status | TEXT | success / failure / partial |
| started_at | TEXT | inicio (obligatorio) |
| ended_at | TEXT | fin (nullable si en progreso) |
| duration_ms | INT | auto-calculado si started_at y ended_at presentes |
| records_in | INT | registros de entrada (nullable) |
| records_out | INT | registros de salida (nullable) |
| error | TEXT | mensaje de error si fallo |
| metrics | JSON{} | metricas custom (ej: {"mean_close": 42000}) |
| created_at | TEXT | RFC3339 |
assertions (9 columnas)
Regla de calidad formal sobre una entity. Evaluable automaticamente contra metadata.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | |
| entity_id | TEXT FK | referencia a entities.id |
| name | TEXT | nombre descriptivo |
| kind | TEXT | tipo libre: range, null, statistical, consistency, freshness, o custom |
| rule | TEXT | expresion SQL evaluable (ver motor de evaluacion abajo) |
| severity | TEXT | critical / warning / info |
| description | TEXT | que verifica |
| active | INT | 0/1 — solo las activas se evaluan |
| created_at | TEXT | RFC3339 |
assertion_results (7 columnas)
Historial de evaluaciones de assertions.
| Columna | Tipo | Descripcion |
|---|---|---|
| id | TEXT PK | |
| assertion_id | TEXT FK | referencia a assertions.id |
| execution_id | TEXT | referencia a executions.id (vacio si eval manual) |
| status | TEXT | pass / fail / skip |
| value | JSON{} | datos capturados en el momento de evaluacion |
| message | TEXT | detalle del resultado |
| evaluated_at | TEXT | RFC3339 |
FTS5 operations
entities_fts— indexa: id, name, description, tags, domainassertions_fts— indexa: id, name, description, rule
Motor de evaluacion de assertions
Las rules se escriben como expresiones SQL. Campos sin prefijo se reescriben automaticamente a json_extract(metadata, '$.campo'):
close > 0 → json_extract(metadata, '$.close') > 0
low <= close AND close <= high → json_extract(metadata, '$.low') <= ...
open IS NOT NULL → json_extract(metadata, '$.open') IS NOT NULL
Si la rule ya usa json_extract, se deja como esta. Palabras SQL (AND, OR, NOT, IS, NULL, BETWEEN, etc) y funciones SQLite (datetime, abs, max, min, etc) no se reescriben.
Kinds documentados (puedes añadir nuevos sin tocar schema):
| Kind | Descripcion | Ejemplo de rule |
|---|---|---|
range |
Valor dentro de rango | close BETWEEN 0 AND 1000000 |
null |
Campo no nulo | open IS NOT NULL |
consistency |
Relacion entre campos | low <= close AND close <= high |
freshness |
Datos recientes | json_extract(metadata, '$.ts') > datetime('now', '-1 hour') |
statistical |
Desviacion estadistica | (evaluar externamente, registrar manual) |
Dos modos:
- Auto:
fn ops assertion eval --entity-id Xejecuta rules SQL contraentities.metadata - Manual:
fn ops assertion result addregistra resultados de assertions que el sistema no puede evaluar
Bucle reactivo (con --react):
fn ops assertion eval --entity-id X --reactevalua Y reacciona:- Critical fail → entity.status =
corrupted+ auto-crea proposal en registry.db - Warning fail → entity.status =
stale(solo si eraactive) - Info fail → sin cambio
- Critical fail → entity.status =
Sistema de migraciones
Ambas BDs usan un sistema de migraciones con embed.FS:
registry/migrations/
001_init.sql # functions + types + FTS
002_proposals.sql # proposals + FTS
fn_operations/migrations/
001_init.sql # entities + relations + relation_inputs + types_snapshot + FTS
002_executions_assertions.sql # executions + assertions + assertion_results + FTS
- Tabla
schema_migrationsen cada BD rastrea versiones aplicadas CREATE TABLE IF NOT EXISTS+ transacciones por migracion = idempotente- Al hacer
Open()se aplican automaticamente las migraciones pendientes - Para añadir una nueva migracion: crear
NNN_nombre.sqlen la carpeta correspondiente
Estructura del repositorio
fn-registry/
functions/ # Codigo y docs de funciones
core/ # Utilidades genericas (filter, map, pipeline, retry...)
finance/ # Indicadores, riesgo, IO de mercado
datascience/ # Estadistica, DSP, IO de datos
cybersecurity/ # Crypto, analisis de red, IO de seguridad
pipelines/ # Composiciones de funciones, siempre impuras
components/ # Componentes React (.tsx)
types/ # Tipos algebraicos (product y sum)
core/ # Result, Option, Pair, Error
finance/ # OHLCV, Tick, BollingerResult, DrawdownResult
datascience/ # OutlierResult
cybersecurity/ # CIDRBlock, ThreatResult, PortResult
registry/ # Paquete Go: modelos, SQLite, parser, indexer, validacion, migraciones
models.go # Function, Type, Proposal structs + enums (Kind, Purity, Algebraic, ProposalKind, ProposalStatus)
db.go # Open/Close/Drop + WAL + migraciones
store.go # CRUD: Insert/Get/Update/Delete/List/Search para functions, types, proposals
validate.go # ValidateFunction, ValidateType, ValidateProposal
parser.go # ParseFunctionMD, ParseTypeMD (YAML frontmatter)
indexer.go # Index() — two-pass: parse → validate refs → insert
migrate.go # Motor de migraciones (embed.FS)
migrations/ # Archivos .sql numerados
fn_operations/ # Paquete Go: operations database (libreria, NO apps)
models.go # Entity, Relation, RelationInput, TypeSnapshot, Execution, Assertion, AssertionResult + enums
db.go # Open/Close/Drop/Conn
store.go # CRUD para todas las tablas
validate.go # ValidateEntity, ValidateRelation, ValidateExecution, ValidateAssertion, DetectCycle
operations.go # Alto nivel: InsertEntityWithSnapshot, InsertRelationSafe, React, ExecuteAndReact
eval.go # Motor de evaluacion: rewriteRule, EvalAssertion, EvalEntityAssertions
migrate.go # Motor de migraciones
migrations/ # Archivos .sql numerados
apps/ # Aplicaciones ejecutables (TUIs, CLIs) — modulos Go independientes
docker_tui/ # TUI fullscreen para gestionar Docker
pipeline_launcher/ # TUI para lanzar pipelines y registrar ejecuciones
cmd/fn/ # CLI
main.go # Subcomandos: index, search, list, show, add, ops, proposal
ops.go # fn ops: entity, relation, graph, snapshot, execution, assertion
proposal.go # fn proposal: add, list, show, update
docs/ # Specs de diseño (fuente de verdad del schema)
docs/templates/ # Plantillas de frontmatter
registry.db # Indice SQLite FTS5+WAL (regenerable con fn index, excepto proposals)
Build
CGO_ENABLED=1 go build -tags fts5 ./...
CGO_ENABLED=1 go test -tags fts5 ./...
CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/
CLI completo (cmd/fn)
Registry
fn index # Regenera registry.db desde los .md
fn search "texto" # Busqueda FTS en functions + types
fn search -k function -p pure -d core "slice"
fn list # Lista todo
fn list -d finance -k function # Lista por dominio y kind
fn show filter_slice_go_core # Muestra entrada completa
fn add -k function # Muestra template para copiar
Proposals
fn proposal add --kind new_function --title "..." --created-by agent [--target-id <id>] [--evidence '{}'] [--description "..."]
fn proposal list [-k kind] [-s status]
fn proposal show <id>
fn proposal update <id> --status approved [--reviewed-by lucas]
Operations
fn ops init [path] # Crea operations.db en el directorio
fn ops help # Ayuda
# Entities
fn ops entity add --id <id> --name <n> --type-ref <tipo> --source <src> [--metadata '{}'] [--domain d] [--tags t1,t2]
fn ops entity list [--domain d] [--status s]
fn ops entity show <id>
fn ops entity delete <id>
# Relations
fn ops relation add --id <id> --name <n> --from <eid> --to <eid> [--via <fn_id>] [--direction uni] [--status designed]
fn ops relation list [--from <eid>]
fn ops relation show <id>
fn ops relation delete <id>
# Graph
fn ops graph # ASCII graph de entities y relations
# Snapshots
fn ops snapshot list # Lista type snapshots
fn ops snapshot check # Compara snapshots vs registry actual
fn ops snapshot update <type_id>|--all # Re-snapshot desde registry
# Executions
fn ops execution add --pipeline-id <id> --status success [--started-at <ts>] [--ended-at <ts>] [--records-in N] [--records-out N] [--metrics '{}'] [--error "msg"]
fn ops execution list [--pipeline-id <id>] [--relation-id <id>] [-s status]
fn ops execution show <id>
# Assertions
fn ops assertion add --entity-id <id> --name "close positivo" --kind range --rule "close > 0" --severity critical [--description "..."]
fn ops assertion list [--entity-id <id>] [--active] [--inactive]
fn ops assertion show <id> # Incluye ultimos 5 resultados
fn ops assertion delete <id>
fn ops assertion eval --entity-id <id> [--execution-id <id>] [--react] # Evalua assertions activas
# Assertion results (registro manual)
fn ops assertion result add --assertion-id <id> --status pass|fail|skip [--execution-id <id>] [--value '{}'] [--message "..."]
fn ops assertion result list [--assertion-id <id>] [--execution-id <id>]
FN_REGISTRY_ROOT env var permite que fn ops acceda a registry.db desde cualquier directorio.
Reglas para añadir funciones nuevas
Antes de crear
- Consulta la BD para verificar que no existe algo similar:
sqlite3 registry.db "SELECT id, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'tu busqueda');" - Identifica el dominio correcto: core, finance, datascience, cybersecurity, o crea uno nuevo si no encaja
- Decide la purity: pure si no tiene side effects, impure si tiene IO/estado/goroutines/tiempo
Archivos a crear
Cada funcion requiere EXACTAMENTE dos archivos:
- Implementacion
functions/{domain}/{name}.go— codigo real, compilable - Documentacion
functions/{domain}/{name}.md— frontmatter YAML con metadata
Formato del .md (frontmatter YAML)
---
name: nombre_snake_case
kind: function # function | pipeline | component
lang: go # go | python | typescript | sql
domain: core # core | finance | datascience | cybersecurity | ...
version: "1.0.0"
purity: pure # pure | impure
signature: "func NombreCompleto(...) ..."
description: "Descripcion en español de que hace y cuando usarla."
tags: [tag1, tag2, tag3]
uses_functions: [] # IDs de funciones del registry que invoca
uses_types: [] # IDs de tipos del registry que recibe
returns: [] # IDs de tipos del registry que devuelve
returns_optional: false
error_type: "" # ID de tipo de error, obligatorio si impure
imports: [] # dependencias externas fuera del registry
tested: false
tests: []
test_file_path: ""
file_path: "functions/{domain}/{name}.go"
---
## Ejemplo
` `` go
resultado := MiFuncion(input)
` ``
## Notas
Explicacion adicional si es necesario.
Reglas de integridad (el indexer las valida)
| Regla | Condicion |
|---|---|
| Pipeline siempre impuro | kind: pipeline → purity: impure + uses_functions no vacio |
| Pura sin side effects | purity: pure → returns_optional: false + error_type: "" |
| Impura declara errores | purity: impure → error_type obligatorio (usar error_go_core) |
| Tests coherentes | tested: true → test_file_path y tests obligatorios |
| Referencias validas | uses_functions, uses_types, returns, error_type deben apuntar a IDs existentes |
| Component tiene framework | kind: component → framework obligatorio, returns vacio (usar emits) |
| Rutas relativas | file_path siempre relativa a la raiz, nunca absoluta |
| IDs unicos | Formato {name}_{lang}_{domain}, colisiones rechazadas |
Campo returns vs tipo nativo
El campo returns en el .md es para IDs de tipos del registry (ej: ohlcv_go_finance), NO para tipos nativos de Go (float64, string, bool). Si la funcion devuelve tipos nativos, deja returns: [].
Despues de crear
# Regenerar el indice
./fn index
# o
CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/ && ./fn index
# Verificar que se indexo sin errores
./fn show {name}_{lang}_{domain}
Reglas para añadir tipos nuevos
Cada tipo requiere dos archivos: types/{domain}/{name}.go y types/{domain}/{name}.md.
---
name: nombre_snake_case
lang: go
domain: core
version: "1.0.0"
algebraic: product # product (struct) | sum (interface/union)
definition: |
type MiTipo struct { ... }
description: "Descripcion en español."
tags: [tag1, tag2]
uses_types: [] # IDs de otros tipos que compone (sin auto-referencias)
file_path: "types/{domain}/{name}.go"
---
Convenciones
- IDs:
{name}_{lang}_{domain}(ej:filter_slice_go_core) - Nombres: snake_case para funciones, PascalCase para tipos en Go
- Paquete Go: el nombre del directorio (core, finance, datascience, cybersecurity)
- Tipos en firmas: usar tipos nativos (float64, []float64, string) para evitar imports circulares entre paquetes de funciones. Documentar los tipos del registry en
uses_types/returnsdel .md - Purity: puras en el centro, impuras en los bordes. Una pura NUNCA depende de una impura
- Stubs impuros: si la implementacion real requiere dependencias externas no disponibles, crear stub con
return ..., fmt.Errorf("not implemented")y documentar completamente el .md - Assertions: kind es texto libre — puedes inventar nuevos kinds sin tocar schema
- Proposals: las crea el bucle reactivo automaticamente (created_by: reactive_loop) o el humano/agente manualmente
Fuentes de verdad
| Que | Donde |
|---|---|
| Codigo | archivos .go / .py / .tsx |
| Documentacion | archivos .md junto al codigo |
| Diseño del schema | carpeta docs/ |
| Indice de busqueda | registry.db (regenerable con fn index) |
| Proposals | registry.db tabla proposals (NO regenerable, son datos vivos) |
| Entities, relations | operations.db por proyecto (datos vivos) |
| Executions, assertions | operations.db por proyecto (datos vivos) |
Importante: fn index regenera functions y types desde los .md pero NO toca proposals. Las proposals, entities, relations, executions, assertions y assertion_results son datos vivos que persisten.
Codigo Go: patrones clave
JSON en columnas TEXT
Arrays y objetos se guardan como JSON serializado en columnas TEXT:
marshalStrings([]string) string // ["a","b"] → string
unmarshalStrings(string) []string // string → ["a","b"]
marshalJSON(map[string]any) string // {k:v} → string
unmarshalJSON(string) map[string]any
Validacion
Acumula errores, retorna nil o *ValidationError:
func ValidateX(x *X) *ValidationError {
var errs []string
if x.Name == "" { errs = append(errs, "name required") }
if len(errs) > 0 { return &ValidationError{ID: x.ID, Errors: errs} }
return nil
}
Migraciones
//go:embed migrations/*.sql
var migrationsFS embed.FS
func migrate(conn *sql.DB) error { ... } // aplica pendientes en transacciones
Cycle detection
Solo relaciones causales (via != "") se verifican. BFS desde to_entity buscando from_entity.