# types

id: cron_schedule_go_core
name: cron_schedule
lang: go
domain: core
version: 1.0.0
algebraic: product
tags: ["cron","schedule","scheduling","time","parsed"]
uses_types: []
file_path: functions/core/cron_schedule.go
created_at: 2026-04-05T11:24:00Z
updated_at: 2026-04-05T11:24:00Z
content_hash: a78f4dfc6c9291f8ac874e0c6caa6cda0047d3ba842aa768c621b1d932ae299c
definition:
  type CronSchedule struct {
      Minute     []int
      Hour       []int
      DayOfMonth []int
      Month      []int
      DayOfWeek  []int
      Raw        string
  }

description:
  Representacion de una expresion cron parseada con los valores expandidos por campo. Raw preserva la expresion original para debug.

notes:
  Tipo producto inmutable por convencion. Cada campo contiene la lista completa de valores validos para ese campo (ej: Minute=[0,15,30,45] para `*/15`). Producido por ParseCronExpr, consumido por NextCronTime y CronTicker.

code:
  package core

  // CronSchedule represents a parsed cron expression with expanded field values.
  type CronSchedule struct {
  	Minute     []int
  	Hour       []int
  	DayOfMonth []int
  	Month      []int
  	DayOfWeek  []int
  	Raw        string // original expression
  }


id: dag_continue_on_go_core
name: dag_continue_on
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type DagContinueOn struct {
  Failure bool
  Skipped bool
  }

tags: ["dag","workflow","policy","error-handling"]
uses_types: []
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
notes: Tipo producto. Embebido en DagStep. Corresponde al campo `continue_on` del YAML de Dagu.
content_hash: 2199c7eb26d32e0cd716355b018f1a216fb17764808f7a28b8a6bc26cc757a95
description:
  Politica de continuacion de un step DAG ante fallos o saltos. Cuando Failure=true el DAG no se detiene si el step falla. Cuando Skipped=true tampoco se detiene si el step es saltado.

code:
  package core

  // DagContinueOn controls whether a step continues on failure/skip.
  type DagContinueOn struct {
  	Failure bool
  	Skipped bool
  }

  // DagRetryPolicy configures automatic retries for a step.
  type DagRetryPolicy struct {
  	Limit       int
  	IntervalSec int
  }

  // DagStep represents a single step in a DAG workflow.
  type DagStep struct {
  	Name        string
  	ID          string
  	Description string
  	Command     string
  	Script      string
  	Args        []string
  	Shell       string
  	Dir         string
  	Depends     []string
  	Env         map[string]string
  	ContinueOn  DagContinueOn
  	RetryPolicy DagRetryPolicy
  	TimeoutSec  int
  	Output      string
  	Tags        []string
  }

  // DagHandlers contains lifecycle handler steps.
  type DagHandlers struct {
  	Init    []DagStep
  	Success []DagStep
  	Failure []DagStep
  	Exit    []DagStep
  }

  // DagDefinition is a complete DAG workflow parsed from YAML.
  type DagDefinition struct {
  	Name        string
  	Description string
  	Group       string
  	Type        string // "graph" or "" (chain/sequential)
  	WorkingDir  string
  	Shell       string
  	Env         map[string]string
  	Schedule    []string
  	Steps       []DagStep
  	HandlerOn   DagHandlers
  	Tags        []string
  	TimeoutSec  int
  	FilePath    string
  }

  // DagValidationResult contains validation output.
  type DagValidationResult struct {
  	Valid    bool
  	Errors   []string
  	Warnings []string
  	Levels   [][]string // topological levels (step names/IDs)
  }


id: dag_definition_go_core
name: dag_definition
lang: go
domain: core
version: 1.0.0
algebraic: product
tags: ["dag","workflow","yaml","dagu","definition"]
uses_types: ["dag_step_go_core","dag_handlers_go_core"]
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: 37f481b8854e12d467e1abd18cb8943f17af2d88b7c92be772a99e06a8712c92
definition:
  type DagDefinition struct {
      Name        string
      Description string
      Group       string
      Type        string
      WorkingDir  string
      Shell       string
      Env         map[string]string
      Schedule    []string
      Steps       []DagStep
      HandlerOn   DagHandlers
      Tags        []string
      TimeoutSec  int
      FilePath    string
  }

description:
  Definicion completa de un workflow DAG parseada desde YAML compatible con Dagu. Contiene steps, handlers de ciclo de vida, variables de entorno, schedule y metadatos del flujo.

notes:
  Tipo producto. Type puede ser "graph" (ejecucion paralela por niveles topologicos) o vacio (cadena secuencial). Schedule es siempre una lista de strings (normalizado desde string o lista en el YAML). FilePath se rellena opcionalmente por el caller para saber el origen del archivo.

code:
  package core

  // DagContinueOn controls whether a step continues on failure/skip.
  type DagContinueOn struct {
  	Failure bool
  	Skipped bool
  }

  // DagRetryPolicy configures automatic retries for a step.
  type DagRetryPolicy struct {
  	Limit       int
  	IntervalSec int
  }

  // DagStep represents a single step in a DAG workflow.
  type DagStep struct {
  	Name        string
  	ID          string
  	Description string
  	Command     string
  	Script      string
  	Args        []string
  	Shell       string
  	Dir         string
  	Depends     []string
  	Env         map[string]string
  	ContinueOn  DagContinueOn
  	RetryPolicy DagRetryPolicy
  	TimeoutSec  int
  	Output      string
  	Tags        []string
  }

  // DagHandlers contains lifecycle handler steps.
  type DagHandlers struct {
  	Init    []DagStep
  	Success []DagStep
  	Failure []DagStep
  	Exit    []DagStep
  }

  // DagDefinition is a complete DAG workflow parsed from YAML.
  type DagDefinition struct {
  	Name        string
  	Description string
  	Group       string
  	Type        string // "graph" or "" (chain/sequential)
  	WorkingDir  string
  	Shell       string
  	Env         map[string]string
  	Schedule    []string
  	Steps       []DagStep
  	HandlerOn   DagHandlers
  	Tags        []string
  	TimeoutSec  int
  	FilePath    string
  }

  // DagValidationResult contains validation output.
  type DagValidationResult struct {
  	Valid    bool
  	Errors   []string
  	Warnings []string
  	Levels   [][]string // topological levels (step names/IDs)
  }


id: dag_handlers_go_core
name: dag_handlers
lang: go
domain: core
version: 1.0.0
algebraic: product
tags: ["dag","workflow","handlers","lifecycle"]
uses_types: ["dag_step_go_core"]
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: 1dc8884d4c6e839c7990bb834ff1be040afe1cdda9f7b2a850aed493ff68fa55
definition:
  type DagHandlers struct {
      Init    []DagStep
      Success []DagStep
      Failure []DagStep
      Exit    []DagStep
  }

description:
  Handlers de ciclo de vida de un DAG. Cada campo contiene steps que se ejecutan en el evento correspondiente: inicializacion, exito, fallo o salida (siempre). Corresponde a handler_on o handlers en el YAML de Dagu.

notes:
  Tipo producto. Embebido en DagDefinition. Los campos handler_on y handlers son aliases en el YAML de Dagu — ambos se normalizan a este tipo. Cada handler puede ser un step unico o una lista de steps en el YAML.

code:
  package core

  // DagContinueOn controls whether a step continues on failure/skip.
  type DagContinueOn struct {
  	Failure bool
  	Skipped bool
  }

  // DagRetryPolicy configures automatic retries for a step.
  type DagRetryPolicy struct {
  	Limit       int
  	IntervalSec int
  }

  // DagStep represents a single step in a DAG workflow.
  type DagStep struct {
  	Name        string
  	ID          string
  	Description string
  	Command     string
  	Script      string
  	Args        []string
  	Shell       string
  	Dir         string
  	Depends     []string
  	Env         map[string]string
  	ContinueOn  DagContinueOn
  	RetryPolicy DagRetryPolicy
  	TimeoutSec  int
  	Output      string
  	Tags        []string
  }

  // DagHandlers contains lifecycle handler steps.
  type DagHandlers struct {
  	Init    []DagStep
  	Success []DagStep
  	Failure []DagStep
  	Exit    []DagStep
  }

  // DagDefinition is a complete DAG workflow parsed from YAML.
  type DagDefinition struct {
  	Name        string
  	Description string
  	Group       string
  	Type        string // "graph" or "" (chain/sequential)
  	WorkingDir  string
  	Shell       string
  	Env         map[string]string
  	Schedule    []string
  	Steps       []DagStep
  	HandlerOn   DagHandlers
  	Tags        []string
  	TimeoutSec  int
  	FilePath    string
  }

  // DagValidationResult contains validation output.
  type DagValidationResult struct {
  	Valid    bool
  	Errors   []string
  	Warnings []string
  	Levels   [][]string // topological levels (step names/IDs)
  }


id: dag_retry_policy_go_core
name: dag_retry_policy
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type DagRetryPolicy struct {
  Limit int
  IntervalSec int
  }

tags: ["dag","workflow","retry","policy"]
uses_types: []
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: 4a8c40235dc9f1818919b55954c36c7a93e0d55fbcb6af962867869f08afd718
description:
  Politica de reintentos automaticos para un step DAG. Limit es el numero maximo de reintentos e IntervalSec es el tiempo de espera entre intentos en segundos.

notes:
  Tipo producto. Embebido en DagStep. Corresponde al campo `retry_policy` del YAML de Dagu. Limit=0 significa sin reintentos.

code:
[cell repeats an earlier one in this file: 1438 characters]


id: dag_step_go_core
name: dag_step
lang: go
domain: core
version: 1.0.0
algebraic: product
tags: ["dag","workflow","step","yaml","dagu"]
uses_types: ["dag_continue_on_go_core","dag_retry_policy_go_core"]
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: fe54b28a53af7b45d9606c4e64c7dc7edfd700d6a110a53201d17a09b0a70875
definition:
  type DagStep struct {
      Name        string
      ID          string
      Description string
      Command     string
      Script      string
      Args        []string
      Shell       string
      Dir         string
      Depends     []string
      Env         map[string]string
      ContinueOn  DagContinueOn
      RetryPolicy DagRetryPolicy
      TimeoutSec  int
      Output      string
      Tags        []string
  }

description:
  Un paso individual en un workflow DAG con command/script, dependencias y configuracion de reintentos. Soporta variables de entorno por step, directorio de trabajo propio y politica continue_on.

notes:
  Tipo producto. El campo ID es opcional y se usa como referencia en otros steps (ej: `${id.stdout}`). Si Name e ID estan ambos vacios, el step es invalido. Env del step se mergea sobre el Env del DAG padre durante la resolucion.

code:
[cell repeats an earlier one in this file: 1438 characters]


id: dag_validation_result_go_core
name: dag_validation_result
lang: go
domain: core
version: 1.0.0
algebraic: product
tags: ["dag","validation","workflow","result"]
uses_types: []
file_path: functions/core/dag_definition.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: f76918b2d9229b43f89075362fda85d2345c87dd88269e5009d4b8af9c86c984
definition:
  type DagValidationResult struct {
      Valid    bool
      Errors   []string
      Warnings []string
      Levels   [][]string
  }

description:
  Resultado de validar un DagDefinition. Contiene errores estructurales (ciclos, depends invalidos, nombres duplicados), warnings (command+script simultaneos) y los niveles topologicos calculados si el DAG es valido.

notes:
  Tipo producto. Levels solo se rellena cuando Valid=true. Cada sub-slice de Levels contiene los nombres/IDs de steps que pueden ejecutarse en paralelo. El orden de Levels refleja el orden topologico del grafo de dependencias.

code:
[cell repeats an earlier one in this file: 1438 characters]


id: document_go_core
name: document
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Documento o archivo de interes. Contiene titulo, formato, clasificacion y hash.
tags: ["document","file","entity"]
uses_types: []
file_path: functions/core/document.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 83ddb66aada2c8451a584a0727bb54a8670c0bd9c3163b42d1044e14e28fa6ae
definition:
  type Document struct {
      Title          string `json:"title"`
      Format         string `json:"format"`
      Classification string `json:"classification"`
      HashSHA256     string `json:"hash_sha256"`
      Source         string `json:"source"`
  }

code:
  package core

  // Document represents a document or file of interest.
  type Document struct {
  	Title          string `json:"title"`
  	Format         string `json:"format"`
  	Classification string `json:"classification"`
  	HashSHA256     string `json:"hash_sha256"`
  	Source         string `json:"source"`
  }


id: domain_go_core
name: domain
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Dominio de internet. Contiene FQDN, registrar y nameservers.
tags: ["domain","dns","infrastructure","entity"]
uses_types: []
file_path: functions/core/domain.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: b5419e889cd5905e74179148fff13a941f762a87e9bded90c3d4860a3a076874
definition:
  type Domain struct {
      FQDN        string   `json:"fqdn"`
      Registrar   string   `json:"registrar"`
      CreatedDate string   `json:"created_date"`
      ExpiresDate string   `json:"expires_date"`
      NameServers []string `json:"name_servers"`
  }

code:
  package core

  // Domain represents an internet domain.
  type Domain struct {
  	FQDN        string   `json:"fqdn"`
  	Registrar   string   `json:"registrar"`
  	CreatedDate string   `json:"created_date"`
  	ExpiresDate string   `json:"expires_date"`
  	NameServers []string `json:"name_servers"`
  }


id: email_go_core
name: email
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Direccion de email de interes. Indica si fue verificada y si aparece en breaches.
tags: ["email","identity","entity"]
uses_types: []
file_path: functions/core/email.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 2f4a644e7afe69074b7be26c496641147bb42bd4ba826ce14fe04cdf8a7efe8b
definition:
  type Email struct {
      Address  string `json:"address"`
      Provider string `json:"provider"`
      Verified bool   `json:"verified"`
      Breached bool   `json:"breached"`
  }

code:
  package core

  // Email represents an email address of interest.
  type Email struct {
  	Address  string `json:"address"`
  	Provider string `json:"provider"`
  	Verified bool   `json:"verified"`
  	Breached bool   `json:"breached"`
  }


id: error_go_core
name: error
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type Error struct {
  Message string
  }

description: Tipo de error base del registry. Referenciado como error_type por funciones impuras.
tags: ["error","base","impure"]
uses_types: []
file_path: functions/core/error.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 35f07b24eb07327f58dd249725d9e71766989f6e5884e9b1d7b0e09c9cee4a5b
code:
  package core

  // Error is the standard error type for impure functions in the registry.
  // Wraps a message string. Used as error_type reference.
  type Error struct {
  	Message string
  }

  func (e Error) Error() string {
  	return e.Message
  }


id: event_go_core
name: event
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Evento o incidente notable. Contiene tipo, fecha, ubicacion y severidad.
tags: ["event","incident","entity"]
uses_types: []
file_path: functions/core/event.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 3b9f49b3ed9d7bc7d217bb7ef5cf3ee123ce3004a6822533ef10f3bfa508ebc4
definition:
  type Event struct {
      EventType   string `json:"event_type"`
      Date        string `json:"date"`
      Location    string `json:"location"`
      Description string `json:"description"`
      Severity    string `json:"severity"`
  }

code:
  package core

  // Event represents a notable event or incident.
  type Event struct {
  	EventType   string `json:"event_type"`
  	Date        string `json:"date"`
  	Location    string `json:"location"`
  	Description string `json:"description"`
  	Severity    string `json:"severity"`
  }


id: location_go_core
name: location
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Ubicacion geografica de interes. Contiene coordenadas, direccion y pais.
tags: ["location","geography","entity"]
uses_types: []
file_path: functions/core/location.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 88e3dafd73cba55e94e29dfac3f5744cab778bdebd0fbc1a6a1a700d08d89b28
definition:
  type Location struct {
      Lat     float64 `json:"lat"`
      Lon     float64 `json:"lon"`
      Address string  `json:"address"`
      Country string  `json:"country"`
      City    string  `json:"city"`
  }

code:
  package core

  // Location represents a geographic location of interest.
  type Location struct {
  	Lat     float64 `json:"lat"`
  	Lon     float64 `json:"lon"`
  	Address string  `json:"address"`
  	Country string  `json:"country"`
  	City    string  `json:"city"`
  }


id: option_go_core
name: option
lang: go
domain: core
version: 1.0.0
algebraic: sum
definition:
  type Option[T any] struct {
  value *T
  }

tags: ["option","sum","nullable","functional","generic"]
uses_types: []
file_path: functions/core/option.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
notes: Tipo suma con dos variantes: Some(T) y None. Inspirado en Option de Rust.
content_hash: cf708c9b9e72b741d3d190942e2cd61c5eb9e27476424a26e97f22cbf08b4709
description:
  Tipo suma generico que representa un valor opcional: Some(T) o None. Alternativa a punteros nil para modelar ausencia de valor de forma explicita.

code:
  package core

  // Option represents a value that may or may not be present.
  // A sum type: Some(T) | None.
  type Option[T any] struct {
  	value *T
  }

  // Some creates an Option containing a value.
  func Some[T any](v T) Option[T] {
  	return Option[T]{value: &v}
  }

  // None creates an empty Option.
  func None[T any]() Option[T] {
  	return Option[T]{}
  }

  // IsSome returns true if the Option contains a value.
  func (o Option[T]) IsSome() bool {
  	return o.value != nil
  }

  // IsNone returns true if the Option is empty.
  func (o Option[T]) IsNone() bool {
  	return o.value == nil
  }

  // Unwrap returns the value or panics if None.
  func (o Option[T]) Unwrap() T {
  	if o.value == nil {
  		panic("called Unwrap on None")
  	}
  	return *o.value
  }

  // UnwrapOr returns the value or a default if None.
  func (o Option[T]) UnwrapOr(def T) T {
  	if o.value != nil {
  		return *o.value
  	}
  	return def
  }


id: organization_go_core
name: organization
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Organizacion o empresa de interes. Contiene datos legales, sector y score de riesgo.
tags: ["organization","company","entity"]
uses_types: []
file_path: functions/core/organization.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 8536fd99c1a9940f121928d5d423ca4c6bab9704db8c0ae29a3855c3558b91fe
definition:
  type Organization struct {
      LegalName string  `json:"legal_name"`
      Country   string  `json:"country"`
      Sector    string  `json:"sector"`
      Founded   string  `json:"founded"`
      RiskScore float64 `json:"risk_score"`
  }

code:
  package core

  // Organization represents a company or group of interest.
  type Organization struct {
  	LegalName string  `json:"legal_name"`
  	Country   string  `json:"country"`
  	Sector    string  `json:"sector"`
  	Founded   string  `json:"founded"`
  	RiskScore float64 `json:"risk_score"`
  }


id: pair_go_core
name: pair
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type Pair[A any, B any] struct {
  First A
  Second B
  }

tags: ["pair","tuple","product","generic"]
uses_types: []
file_path: functions/core/pair.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 28511cc15cf5db62e6c5de797da42f3fe4db9068ee1ed531f90016d2c5f74397
description:
  Tipo producto generico que agrupa dos valores de tipos potencialmente distintos. Util para ZipSlices y operaciones que devuelven dos resultados.

code:
  package core

  // Pair holds two values of potentially different types.
  type Pair[A any, B any] struct {
  	First  A
  	Second B
  }


id: person_go_core
name: person
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Persona de interes. Contiene identidad, nacionalidad y score de riesgo.
tags: ["person","identity","entity"]
uses_types: []
file_path: functions/core/person.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 774c47197c07e37b578b9e8a30e189359dff57f6eea5f6510462d7d5807943b0
definition:
  type Person struct {
      FullName    string   `json:"full_name"`
      Alias       []string `json:"alias"`
      Nationality string   `json:"nationality"`
      DOB         string   `json:"dob"`
      Gender      string   `json:"gender"`
      RiskScore   float64  `json:"risk_score"`
  }

code:
  package core

  // Person represents an individual of interest.
  type Person struct {
  	FullName    string   `json:"full_name"`
  	Alias       []string `json:"alias"`
  	Nationality string   `json:"nationality"`
  	DOB         string   `json:"dob"`
  	Gender      string   `json:"gender"`
  	RiskScore   float64  `json:"risk_score"`
  }


id: phone_go_core
name: phone
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Numero de telefono de interes. Contiene carrier, codigo de pais y tipo.
tags: ["phone","telecom","identity","entity"]
uses_types: []
file_path: functions/core/phone.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: c6eab8641cccc086bedd03f11b188815dbb9a1afcc5bb4086c76526f09fcce42
definition:
  type Phone struct {
      Number      string `json:"number"`
      CountryCode string `json:"country_code"`
      Carrier     string `json:"carrier"`
      PhoneType   string `json:"phone_type"`
  }

code:
  package core

  // Phone represents a phone number of interest.
  type Phone struct {
  	Number      string `json:"number"`
  	CountryCode string `json:"country_code"`
  	Carrier     string `json:"carrier"`
  	PhoneType   string `json:"phone_type"`
  }


id: result_go_core
name: result
lang: go
domain: core
version: 1.0.0
algebraic: sum
definition:
  type Result[T any] struct {
  ok *T
  err error
  }

tags: ["result","sum","error-handling","functional","generic"]
uses_types: []
file_path: functions/core/result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 9e9747db117d656a63baa6b98c49e4327e5ed40d1a85fc681840ce473a5bab6d
description:
  Tipo suma generico que representa exito (Ok) o fallo (Err). Permite componer operaciones que pueden fallar sin recurrir a multiples returns (T, error).

notes:
  Tipo suma con dos variantes: Ok(T) y Err(error). Inspirado en Result de Rust.
  Util para encadenar operaciones y evitar el patron `if err != nil` repetitivo.

code:
  package core

  // Result is a sum type representing either a successful value or an error.
  // Exactly one of Ok or Err is non-nil at any time.
  type Result[T any] struct {
  	ok  *T
  	err error
  }

  // Ok creates a successful Result.
  func Ok[T any](v T) Result[T] {
  	return Result[T]{ok: &v}
  }

  // Err creates a failed Result.
  func Err[T any](err error) Result[T] {
  	return Result[T]{err: err}
  }

  // IsOk returns true if the Result contains a value.
  func (r Result[T]) IsOk() bool {
  	return r.ok != nil
  }

  // IsErr returns true if the Result contains an error.
  func (r Result[T]) IsErr() bool {
  	return r.err != nil
  }

  // Unwrap returns the value or panics if the Result is an error.
  func (r Result[T]) Unwrap() T {
  	if r.ok == nil {
  		panic("called Unwrap on an Err Result")
  	}
  	return *r.ok
  }

  // UnwrapErr returns the error or panics if the Result is Ok.
  func (r Result[T]) UnwrapErr() error {
  	if r.err == nil {
  		panic("called UnwrapErr on an Ok Result")
  	}
  	return r.err
  }

  // UnwrapOr returns the value or a default if the Result is an error.
  func (r Result[T]) UnwrapOr(def T) T {
  	if r.ok != nil {
  		return *r.ok
  	}
  	return def
  }


id: social_media_go_core
name: social_media
lang: go
domain: core
version: 1.0.0
algebraic: product
description: Cuenta de red social de interes. Contiene plataforma, usuario y seguidores.
tags: ["social_media","identity","entity"]
uses_types: []
file_path: functions/core/social_media.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: df41c70eef05ff8a8623259f0fb55b883f2284665b7673013732e4c14cc7cd0a
definition:
  type SocialMedia struct {
      Platform  string `json:"platform"`
      Username  string `json:"username"`
      URL       string `json:"url"`
      Followers int    `json:"followers"`
      Verified  bool   `json:"verified"`
  }

code:
  package core

  // SocialMedia represents a social media account of interest.
  type SocialMedia struct {
  	Platform  string `json:"platform"`
  	Username  string `json:"username"`
  	URL       string `json:"url"`
  	Followers int    `json:"followers"`
  	Verified  bool   `json:"verified"`
  }


id: test_db_go_core
name: test_db
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type TestDB struct {
  DB *sql.DB
  Path string
  Cleanup func()
  }

tags: ["testing","database","sqlite","test_helper"]
uses_types: []
file_path: functions/core/test_db.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 478509524565e687271bdf9cee7d8bd4b5b5d7cfb84f57a8e37d379e69418fd5
description:
  Base de datos SQLite de test con puntero al DB abierto, path (vacio para :memory:) y funcion de cleanup para cerrar conexiones al finalizar el test.

notes:
  Tipo producto — todos los campos siempre presentes. Retornado por `test_db_setup_go_core`.
  Path es `:memory:` para bases de datos en memoria.
  Cleanup se registra automaticamente en t.Cleanup() al crear el DB.

code:
  package core

  import (
  	"database/sql"
  )

  // TestDB encapsula una base de datos SQLite de test con su path y cleanup.
  type TestDB struct {
  	DB      *sql.DB
  	Path    string
  	Cleanup func()
  }


id: test_server_go_core
name: test_server
lang: go
domain: core
version: 1.0.0
algebraic: product
definition:
  type TestServer struct {
  URL string
  Client *http.Client
  Cleanup func()
  }

tags: ["testing","http","server","test_helper"]
uses_types: []
file_path: functions/core/test_server.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: d4b96905efeb2e66e68f3944fcaf67a2b7677c7fa5e19b8c1ecb0930ba49299b
description:
  Servidor HTTP de test con URL base, cliente preconfigurado y funcion de cleanup para liberar recursos al finalizar el test.

notes:
  Tipo producto — todos los campos siempre presentes. Retornado por `test_http_server_go_core`.
  Cleanup se registra automaticamente en t.Cleanup() al crear el servidor.

code:
  package core

  import (
  	"net/http"
  )

  // TestServer encapsula un servidor HTTP de test con su cliente y cleanup.
  type TestServer struct {
  	URL     string
  	Client  *http.Client
  	Cleanup func()
  }


id: cidr_block_go_cybersecurity
name: cidr_block
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: product
definition:
  type CIDRBlock struct {
  Network *net.IPNet
  Broadcast net.IP
  Hosts int
  }

description: Rango de red CIDR parseado con network, broadcast y numero de hosts.
tags: ["cybersecurity","network","cidr","ip"]
uses_types: []
file_path: functions/cybersecurity/cidr_block.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 79c4dce13e6f3a58953613cea7d216eb25dc8d001e6f40be8b143eb550df17e2
code:
  package cybersecurity

  import "net"

  // CIDRBlock represents a parsed CIDR network range.
  type CIDRBlock struct {
  	Network   *net.IPNet
  	Broadcast net.IP
  	Hosts     int
  }


id: crypto_wallet_go_cybersecurity
name: crypto_wallet
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: product
tags: ["osint","crypto","wallet","blockchain","intelligence"]
uses_types: []
file_path: functions/cybersecurity/crypto_wallet.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: a69a5b010f8ed13fde370bde992537e94b611df5d6648ed384a3ccd424174639
definition:
  type CryptoWallet struct {
      Address    string  `json:"address"`
      Blockchain string  `json:"blockchain"`
      Balance    float64 `json:"balance"`
      FirstSeen  string  `json:"first_seen"`
      LastSeen   string  `json:"last_seen"`
  }

description:
  Wallet de criptomonedas rastreada en una investigacion OSINT. Contiene direccion, blockchain, balance y timestamps.

code:
  package cybersecurity

  // CryptoWallet represents a cryptocurrency wallet tracked in an OSINT investigation.
  type CryptoWallet struct {
  	Address    string  `json:"address"`
  	Blockchain string  `json:"blockchain"`
  	Balance    float64 `json:"balance"`
  	FirstSeen  string  `json:"first_seen"`
  	LastSeen   string  `json:"last_seen"`
  }


id: ip_address_go_cybersecurity
name: ip_address
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: product
description: Direccion IP de interes en una investigacion OSINT. Contiene ASN, geolocalizacion e ISP.
tags: ["osint","ip","network","infrastructure","intelligence"]
uses_types: []
file_path: functions/cybersecurity/ip_address.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: 30b90c8b8336830223af639b1d0838bf7f456e7fd3f1a1e8a445c7c381a2cc81
definition:
  type IPAddress struct {
      IP          string `json:"ip"`
      ASN         string `json:"asn"`
      Country     string `json:"country"`
      ISP         string `json:"isp"`
      Geolocation string `json:"geolocation"`
      LastSeen    string `json:"last_seen"`
  }

code:
  package cybersecurity

  // IPAddress represents an IP address of interest in an OSINT investigation.
  type IPAddress struct {
  	IP          string `json:"ip"`
  	ASN         string `json:"asn"`
  	Country     string `json:"country"`
  	ISP         string `json:"isp"`
  	Geolocation string `json:"geolocation"`
  	LastSeen    string `json:"last_seen"`
  }


id: malware_go_cybersecurity
name: malware
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: product
tags: ["osint","malware","threat","intelligence"]
uses_types: []
file_path: functions/cybersecurity/malware.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: a0f13f6c6aa641d164c620eb8b21c9ec21894e10e59ded893a9b248844d858a8
definition:
  type Malware struct {
      Family      string `json:"family"`
      HashSHA256  string `json:"hash_sha256"`
      FirstSeen   string `json:"first_seen"`
      LastSeen    string `json:"last_seen"`
      ThreatLevel string `json:"threat_level"`
  }

description:
  Muestra de malware rastreada en una investigacion OSINT. Contiene familia, hash SHA256 y nivel de amenaza.

code:
  package cybersecurity

  // Malware represents a malware sample tracked in an OSINT investigation.
  type Malware struct {
  	Family      string `json:"family"`
  	HashSHA256  string `json:"hash_sha256"`
  	FirstSeen   string `json:"first_seen"`
  	LastSeen    string `json:"last_seen"`
  	ThreatLevel string `json:"threat_level"`
  }


id: port_result_go_cybersecurity
name: port_result
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: sum
description: Tipo suma para resultados de escaneo TCP: Open (con banner), Closed o Filtered.
tags: ["cybersecurity","port","scan","network"]
uses_types: []
file_path: functions/cybersecurity/port_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 78347b0034562ba4e48877f6e2707fc26beb1f592ba1ab6f5ee4fb568164ca72
definition:
  type PortResult interface{ portResult() }
  type PortOpen struct{ Port int; Banner string }
  type PortClosed struct{ Port int }
  type PortFiltered struct{ Port int }

code:
  package cybersecurity

  // PortResult is a sum type for TCP port scan results.
  type PortResult interface{ portResult() }

  // PortOpen indicates the port accepted a connection.
  type PortOpen struct {
  	Port    int
  	Banner  string
  }

  // PortClosed indicates the port refused the connection.
  type PortClosed struct {
  	Port int
  }

  // PortFiltered indicates the port did not respond (timeout).
  type PortFiltered struct {
  	Port int
  }

  func (PortOpen) portResult()     {}
  func (PortClosed) portResult()   {}
  func (PortFiltered) portResult() {}


id: threat_result_go_cybersecurity
name: threat_result
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: sum
description: Tipo suma para resultados de deteccion de amenazas: Clean, Suspicious o Malicious.
tags: ["cybersecurity","threat","detection","sqli"]
uses_types: []
file_path: functions/cybersecurity/threat_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: f0ab76e178120bd198fafbbcae88242d5ebb931c08313f58e24aa20d7707c97e
definition:
  type ThreatResult interface{ threatResult() }
  type Clean struct{}
  type Suspicious struct{ Reason string }
  type Malicious struct{ Pattern string }

code:
  package cybersecurity

  // ThreatResult is a sum type for SQL injection detection results.
  type ThreatResult interface{ threatResult() }

  // Clean indicates no threat was detected.
  type Clean struct{}

  // Suspicious indicates a possible threat with a reason.
  type Suspicious struct {
  	Reason string
  }

  // Malicious indicates a confirmed threat pattern.
  type Malicious struct {
  	Pattern string
  }

  func (Clean) threatResult()      {}
  func (Suspicious) threatResult() {}
  func (Malicious) threatResult()  {}


id: vulnerability_go_cybersecurity
name: vulnerability
lang: go
domain: cybersecurity
version: 1.0.0
algebraic: product
tags: ["osint","vulnerability","cve","threat","intelligence"]
uses_types: []
file_path: functions/cybersecurity/vulnerability.go
created_at: 2026-04-06T14:00:53Z
updated_at: 2026-04-06T14:00:53Z
content_hash: b4042546e36b2434b81783d40637aef0b1c588aa5d3fbba204618b24eef1b36b
definition:
  type Vulnerability struct {
      CVEID           string  `json:"cve_id"`
      CVSS            float64 `json:"cvss"`
      AffectedProduct string  `json:"affected_product"`
      Published       string  `json:"published"`
      Exploited       bool    `json:"exploited"`
  }

description:
  Vulnerabilidad CVE rastreada en una investigacion OSINT. Contiene CVSS score y estado de explotacion.

code:
  package cybersecurity

  // Vulnerability represents a CVE or security vulnerability in an OSINT investigation.
  type Vulnerability struct {
  	CVEID           string  `json:"cve_id"`
  	CVSS            float64 `json:"cvss"`
  	AffectedProduct string  `json:"affected_product"`
  	Published       string  `json:"published"`
  	Exploited       bool    `json:"exploited"`
  }


id: outlier_result_go_datascience
name: outlier_result
lang: go
domain: datascience
version: 1.0.0
algebraic: sum
description: Tipo suma que clasifica un dato como Normal o Outlier con su z-score. Usado por DetectOutliers.
tags: ["datascience","outlier","anomaly","statistics"]
uses_types: []
file_path: functions/datascience/outlier_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 95ec4fb4e68c172281b3d94819fe2ab30987d7c935076d4569abfb5fa7c19b3c
definition:
  type OutlierResult interface{ outlierResult() }
  type Normal struct { Index int; Value float64 }
  type Outlier struct { Index int; Value float64; ZScore float64 }

code:
  package datascience

  // OutlierResult is a sum type representing whether a data point is normal or an outlier.
  type OutlierResult interface{ outlierResult() }

  // Normal indicates the data point is within expected range.
  type Normal struct {
  	Index int
  	Value float64
  }

  // Outlier indicates the data point is anomalous.
  type Outlier struct {
  	Index  int
  	Value  float64
  	ZScore float64
  }

  func (Normal) outlierResult()  {}
  func (Outlier) outlierResult() {}


id: compose_project_go_docker
name: compose_project
lang: go
domain: docker
version: 1.0.0
algebraic: product
description: Proyecto Docker Compose con nombre, archivos de configuracion y lista de servicios.
tags: ["docker","compose","infra","orchestration"]
uses_types: []
file_path: types/docker/compose_project.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: bca8ad203ab561adab6abe81c0dbcae29dc8817ff8bd8f060b5cd6062212d8a4
definition:
  type ComposeProject struct {
      Name        string
      ConfigFiles string
      Services    []string
  }

code:
  package docker

  // ComposeProject representa un proyecto Docker Compose con sus servicios.
  type ComposeProject struct {
  	Name        string
  	ConfigFiles string
  	Services    []string
  }


id: container_go_docker
name: container
lang: go
domain: docker
version: 1.0.0
algebraic: product
description: Contenedor Docker con ID, nombre, imagen, estado y puertos expuestos.
tags: ["docker","container","infra"]
uses_types: []
file_path: types/docker/container.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 7c83601bcb310a82b353dab1537f919ed2ec62aa05d84958378f1f7e4f9423b2
definition:
  type Container struct {
      ID     string
      Names  string
      Image  string
      Status string
      State  string
      Ports  string
  }

code:
  package docker

  // Container representa un contenedor Docker con su estado y configuracion.
  type Container struct {
  	ID     string
  	Names  string
  	Image  string
  	Status string
  	State  string
  	Ports  string
  }


id: image_go_docker
name: image
lang: go
domain: docker
version: 1.0.0
algebraic: product
description: Imagen Docker con repositorio, tag, tamaño y fecha de creacion.
tags: ["docker","image","infra"]
uses_types: []
file_path: types/docker/image.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 0dd4502536f4401923af81b0d07a218af3bc123eae4107d272dc450fec1c8729
definition:
  type Image struct {
      ID         string
      Repository string
      Tag        string
      Size       string
      CreatedAt  string
  }

code:
  package docker

  // Image representa una imagen Docker con su repositorio, tag y tamaño.
  type Image struct {
  	ID         string
  	Repository string
  	Tag        string
  	Size       string
  	CreatedAt  string
  }


id: network_go_docker
name: network
lang: go
domain: docker
version: 1.0.0
algebraic: product
definition:
  type Network struct {
  ID string
  Name string
  Driver string
  Scope string
  }

description: Red Docker con nombre, driver y scope (local/global).
tags: ["docker","network","infra"]
uses_types: []
file_path: types/docker/network.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 6e2a83a5a18a201295ee27d0b112560e7179a7ccb13e5a1119a26295aaac8f5f
code:
  package docker

  // Network representa una red Docker con nombre, driver y scope.
  type Network struct {
  	ID     string
  	Name   string
  	Driver string
  	Scope  string
  }


id: volume_go_docker
name: volume
lang: go
domain: docker
version: 1.0.0
algebraic: product
definition:
  type Volume struct {
  Name string
  Driver string
  Mountpoint string
  }

description: Volumen Docker con nombre, driver y punto de montaje en el host.
tags: ["docker","volume","storage","infra"]
uses_types: []
file_path: types/docker/volume.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: b7554610ffb25e6fbc74f544d8e83ceb3d59fb5237009215069d5447900b9933
code:
  package docker

  // Volume representa un volumen Docker con nombre, driver y punto de montaje.
  type Volume struct {
  	Name       string
  	Driver     string
  	Mountpoint string
  }


id: bollinger_result_go_finance
name: bollinger_result
lang: go
domain: finance
version: 1.0.0
algebraic: product
definition:
  type BollingerResult struct {
  Upper []float64
  Middle []float64
  Lower []float64
  }

description: Resultado de Bollinger Bands con bandas superior, media e inferior.
tags: ["finance","bollinger","indicator","bands"]
uses_types: []
file_path: functions/finance/bollinger_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 7182d51a07eaff448efcdc618e9aac2ae31c41da279536739832c8a0f9a112a1
code:
  package finance

  // BollingerResult holds the three bands of a Bollinger Bands calculation.
  type BollingerResult struct {
  	Upper  []float64
  	Middle []float64
  	Lower  []float64
  }


id: drawdown_result_go_finance
name: drawdown_result
lang: go
domain: finance
version: 1.0.0
algebraic: product
definition:
  type DrawdownResult struct {
  Value float64
  Start int
  End int
  }

description: Resultado de maximo drawdown con el valor de caida y los indices de inicio y fin.
tags: ["finance","drawdown","risk","metric"]
uses_types: []
file_path: functions/finance/drawdown_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 0c3aba3c519ec64a7efdb61585caa4ed1c36d800a38b36ba8616f829a9e16a5d
code:
  package finance

  // DrawdownResult holds the maximum drawdown value and the indices where it occurred.
  type DrawdownResult struct {
  	Value float64
  	Start int
  	End   int
  }


id: ohlcv_go_finance
name: ohlcv
lang: go
domain: finance
version: 1.0.0
algebraic: product
description: Vela de mercado con precios de apertura, maximo, minimo, cierre y volumen.
tags: ["finance","market","candle","ohlcv"]
uses_types: []
file_path: functions/finance/ohlcv.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 61fa8cb8c4fec6ca207a7e6b4b822fe5a8149bf656fcab65c46aa11db54af098
definition:
  type OHLCV struct {
      Open   float64
      High   float64
      Low    float64
      Close  float64
      Volume float64
  }

code:
  package finance

  // OHLCV represents a market candle with open, high, low, close prices and volume.
  type OHLCV struct {
  	Open   float64
  	High   float64
  	Low    float64
  	Close  float64
  	Volume float64
  }


id: tick_go_finance
name: tick
lang: go
domain: finance
version: 1.0.0
algebraic: product
description: Evento de trade individual en un mercado. Contiene simbolo, precio, volumen y timestamp.
tags: ["finance","market","tick","trade"]
uses_types: []
file_path: functions/finance/tick.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: a1661b780e727224d5bd8ec59a73804f94578ecd85f0efb06869e85fbbb53116
definition:
  type Tick struct {
      Symbol    string
      Price     float64
      Volume    float64
      Timestamp time.Time
  }

code:
  package finance

  import "time"

  // Tick represents a single trade event in a market.
  type Tick struct {
  	Symbol    string
  	Price     float64
  	Volume    float64
  	Timestamp time.Time
  }


id: config_error_go_infra
name: config_error
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type ConfigError struct {
  Field string
  Message string
  Tag string
  }

tags: ["config","validation","error","infra"]
uses_types: []
file_path: functions/infra/config_error.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 97fa1d2a27f17f755f69a4c9b7ff94363651b08c7a3dcf5e0aaba8ec68c366ed
description:
  Error de validacion de un campo de configuracion. Field identifica el campo, Message describe el problema, Tag indica la regla que fallo (required, format, etc.).

notes:
  Tipo producto — todos los campos siempre presentes. Se acumula en ConfigValidation.Errors. Tag toma valores como "required" o "format" para que el consumidor pueda filtrar por tipo de error.

code:
  package infra

  // ConfigError represents a single validation error for a configuration field.
  type ConfigError struct {
  	Field   string // struct field name or env var name
  	Message string // human-readable error description
  	Tag     string // validation tag that triggered the error (e.g. "required", "format")
  }


id: config_validation_go_infra
name: config_validation
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type ConfigValidation struct {
  Errors []ConfigError
  IsValid bool
  }

tags: ["config","validation","result","infra"]
uses_types: ["config_error_go_infra"]
file_path: functions/infra/config_validation.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 234ad1b71e0d2c1e194d96aada5ad07e9e5e029ce0d1684676c6fdb65b006426
description:
  Resultado de validar una struct de configuracion. Contiene todos los errores acumulados y un bool de conveniencia. IsValid es true solo cuando Errors esta vacio.

notes:
  Tipo producto. IsValid se computa derivadamente de len(Errors)==0. Permite al consumidor tanto iterar sobre todos los errores como hacer un check rapido con IsValid.

code:
  package infra

  // ConfigValidation holds the result of validating a configuration struct.
  type ConfigValidation struct {
  	Errors  []ConfigError // all validation errors found; empty if valid
  	IsValid bool          // true if Errors is empty
  }


id: container_info_go_infra
name: container_info
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Información básica de un contenedor Docker: ID, nombre, imagen, estado, puertos, labels.
tags: ["docker","container","infra"]
uses_types: []
file_path: functions/infra/container_info.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 1b88ea49550e8787b7fcd0b8ee6130fef5bd776dd8f42bca53e9c24ed3c6634c
definition:
  type ContainerInfo struct {
    ID      string
    Name    string
    Image   string
    Status  string
    State   string
    Ports   string
    Created string
    Labels  map[string]string
  }

examples:
  ```go
  c := ContainerInfo{
      ID:     "abc123",
      Name:   "my-app",
      Image:  "nginx:latest",
      Status: "Up 2 hours",
      State:  "running",
      Ports:  "0.0.0.0:8080->80/tcp",
  }
  ```

notes:
  Tipo product que modela la salida de `docker ps`. Los campos mapean directamente a las columnas del formato JSON de Docker.

code:
  package infra

  // ContainerInfo representa la información básica de un contenedor Docker.
  type ContainerInfo struct {
  	ID      string            // ID corto del contenedor
  	Name    string            // Nombre del contenedor
  	Image   string            // Imagen usada
  	Status  string            // Estado actual (running, exited, etc.)
  	State   string            // Estado detallado
  	Ports   string            // Mapeo de puertos
  	Created string            // Fecha de creación
  	Labels  map[string]string // Labels del contenedor
  }


id: DagRun_go_infra
name: DagRun
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Representa una ejecucion de un workflow DAG. Almacenado en SQLite con estado, timestamps y trigger.
tags: ["dag","execution","run","workflow"]
uses_types: []
file_path: functions/infra/dag_run.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: f22d2ac83a2680b3a73c71e936d32bd4e89702cacd5862e117dd6ecac250b69b
definition:
  type DagRun struct {
      ID         string
      DagName    string
      DagPath    string
      Status     string
      StartedAt  time.Time
      FinishedAt time.Time
      Trigger    string
      Error      string
  }

notes:
  Status puede ser: pending, running, success, failed, cancelled.
  Trigger puede ser: manual, cron, api.

code:
  package infra

  import "time"

  // DagRun represents one execution of a DAG workflow.
  type DagRun struct {
  	ID         string
  	DagName    string
  	DagPath    string
  	Status     string // pending, running, success, failed, cancelled
  	StartedAt  time.Time
  	FinishedAt time.Time
  	Trigger    string // manual, cron, api
  	Error      string
  }

  // DagStepResult represents the outcome of one step within a DagRun.
  type DagStepResult struct {
  	ID         string
  	RunID      string
  	StepName   string
  	Status     string // pending, running, success, failed, skipped
  	ExitCode   int
  	Stdout     string
  	Stderr     string
  	StartedAt  time.Time
  	FinishedAt time.Time
  	DurationMs int64
  	Error      string
  }


id: DagStepResult_go_infra
name: DagStepResult
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["dag","execution","step","result"]
uses_types: ["DagRun_go_infra"]
file_path: functions/infra/dag_run.go
created_at: 2026-04-12T11:03:41Z
updated_at: 2026-04-12T11:03:41Z
notes: Status puede ser: pending, running, success, failed, skipped.
content_hash: 533c9a29876b9c0cafa64272d42f2aa3696c53cf37f42690627ad60dd4c953cd
definition:
  type DagStepResult struct {
      ID         string
      RunID      string
      StepName   string
      Status     string
      ExitCode   int
      Stdout     string
      Stderr     string
      StartedAt  time.Time
      FinishedAt time.Time
      DurationMs int64
      Error      string
  }

description:
  Resultado de la ejecucion de un step individual dentro de un DagRun. Captura exit code, stdout, stderr y duracion.

code:
  package infra

  import "time"

  // DagRun represents one execution of a DAG workflow.
  type DagRun struct {
  	ID         string
  	DagName    string
  	DagPath    string
  	Status     string // pending, running, success, failed, cancelled
  	StartedAt  time.Time
  	FinishedAt time.Time
  	Trigger    string // manual, cron, api
  	Error      string
  }

  // DagStepResult represents the outcome of one step within a DagRun.
  type DagStepResult struct {
  	ID         string
  	RunID      string
  	StepName   string
  	Status     string // pending, running, success, failed, skipped
  	ExitCode   int
  	Stdout     string
  	Stderr     string
  	StartedAt  time.Time
  	FinishedAt time.Time
  	DurationMs int64
  	Error      string
  }


id: db_config_go_infra
name: db_config
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Parametros de conexion para cualquier base de datos soportada. Agnóstico al driver.
tags: ["database","config","connection","sqlite","duckdb","postgres","clickhouse"]
uses_types: []
file_path: functions/infra/db_config.go
created_at: 2026-04-01T18:31:19Z
updated_at: 2026-04-01T18:31:19Z
content_hash: 87dbf1bea34df9821f2c850af62b50d23e833eb6365aa71e930a9b01e095ec44
definition:
  type DBConfig struct {
      Driver string            // "sqlite", "duckdb", "postgres", "clickhouse"
      DSN    string            // Data source name / connection string
      Opts   map[string]string // Driver-specific options (optional)
  }

notes:
  Tipo producto — todos los campos siempre presentes. Driver toma uno de los valores: "sqlite", "duckdb", "postgres", "clickhouse". DSN es el connection string nativo del driver. Opts permite pasar opciones adicionales especificas del driver sin necesidad de un tipo por driver.

code:
  package infra

  // DBConfig holds connection parameters for any supported database.
  type DBConfig struct {
  	Driver string            // "sqlite", "duckdb", "postgres", "clickhouse"
  	DSN    string            // Data source name / connection string
  	Opts   map[string]string // Driver-specific options (optional)
  }


id: DeployConfig_go_infra
name: DeployConfig
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["deploy","config","vps","remote","infra"]
uses_types: []
file_path: functions/infra/deploy_config.go
created_at: 2026-04-12T12:48:21Z
updated_at: 2026-04-12T12:48:21Z
content_hash: 23863a73d908876ce0ed34e5950b46339bd895cb71c07806fad189481ac2dd31
definition:
  type DeployConfig struct { AppName, LocalDir, RemoteDir, BinaryName, BuildCmd, ServiceUser string; Port int; HealthPath string; Env map[string]string }

description:
  Parametriza un deploy de app a un VPS remoto. Agrupa nombre, rutas, build, servicio, puerto, health check y env vars.

examples:
  ```go
  cfg := DeployConfig{
      AppName:     "dag_engine",
      LocalDir:    "apps/dag_engine",
      RemoteDir:   "/opt/apps/dag_engine",
      BinaryName:  "dag_engine",
      BuildCmd:    "CGO_ENABLED=0 GOOS=linux go build -o dag_engine .",
      ServiceUser: "deploy",
      Port:        8080,
      HealthPath:  "/api/health",
      Env:         map[string]string{"DB_PATH": "/opt/apps/dag_engine/data/ops.db"},
  }
  ```

notes:
  Usado por los pipelines `setup_vps_app` y `deploy_app_remote`. El campo `BuildCmd` se ejecuta localmente con `bash -c` en el directorio `LocalDir`. Si `HealthPath` está vacío o `Port` es 0, se omite el health check.

code:
  package infra

  // DeployConfig parametriza un deploy de app a un VPS remoto.
  type DeployConfig struct {
  	AppName     string            // nombre de la app (usado para systemd unit y logging)
  	LocalDir    string            // directorio local de la app (ej: apps/dag_engine)
  	RemoteDir   string            // directorio destino en el VPS (ej: /opt/apps/dag_engine)
  	BinaryName  string            // nombre del binario compilado (ej: dag_engine)
  	BuildCmd    string            // comando de build (ej: CGO_ENABLED=0 GOOS=linux go build -o dag_engine .)
  	ServiceUser string            // usuario del sistema para el servicio (vacío = sin crear)
  	Port        int               // puerto del servicio (0 si no expone HTTP)
  	HealthPath  string            // path del health check (ej: /api/health, vacío = sin check)
  	Env         map[string]string // variables de entorno para el servicio
  }


id: EmailAttachment_go_infra
name: EmailAttachment
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Archivo adjunto a un email. Contiene nombre, MIME type y datos binarios.
tags: ["email","smtp","attachment","mime"]
uses_types: []
file_path: functions/infra/email_types.go
created_at: 2026-04-13T00:03:20Z
updated_at: 2026-04-13T00:03:20Z
content_hash: 031684c4781cb0da517df7e9ccd2caa36610f0b89818794ac8a4d216b78161f0
definition:
  type EmailAttachment struct {
      Filename    string
      ContentType string
      Data        []byte
  }

examples:
  ```go
  att := EmailAttachment{
      Filename:    "report.pdf",
      ContentType: "application/pdf",
      Data:        pdfBytes,
  }
  ```

notes:
  Tipo producto — todos los campos obligatorios. `Data` es el contenido binario crudo; se codifica en base64 al serializar en MIME. Usado por `EmailMessage.Attachments`.

code:
  package infra

  // EmailAttachment represents a file attached to an email.
  type EmailAttachment struct {
  	Filename    string // nombre del archivo (ej: "report.pdf")
  	ContentType string // MIME type (ej: "application/pdf", "image/png")
  	Data        []byte // contenido binario del adjunto
  }

  // EmailMessage is an immutable email ready to send.
  // Build with EmailBuildHTML, EmailBuildText, or EmailWithAttachment.
  type EmailMessage struct {
  	From        string            // direccion del remitente (ej: "Alice <alice@example.com>")
  	To          []string          // destinatarios principales
  	CC          []string          // copia (puede ser nil)
  	BCC         []string          // copia oculta (puede ser nil)
  	Subject     string            // asunto del mensaje
  	BodyHTML    string            // cuerpo HTML (puede ser vacio si BodyText esta presente)
  	BodyText    string            // cuerpo texto plano (puede ser vacio si BodyHTML esta presente)
  	Attachments []EmailAttachment // adjuntos (puede ser nil)
  	Headers     map[string]string // headers MIME adicionales (ej: "X-Mailer": "fn_registry")
  }

  // SMTPConfig holds connection parameters for an SMTP server.
  // TLSMode controls the encryption strategy:
  //
  //	"tls"      — TLS directo (port 465)
  //	"starttls" — STARTTLS upgrade (port 587)
  //	""         — sin cifrado (port 25)
  type SMTPConfig struct {
  	Host     string // hostname del servidor SMTP (ej: "smtp.gmail.com")
  	Port     int    // puerto (465, 587, 25)
  	Username string // usuario/email de autenticacion
  	Password string // contrasena o app password
  	TLSMode  string // "tls", "starttls" o "" (sin cifrado)
  }


id: EmailMessage_go_infra
name: EmailMessage
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["email","smtp","message","mime"]
uses_types: ["EmailAttachment_go_infra"]
file_path: functions/infra/email_types.go
created_at: 2026-04-13T00:03:20Z
updated_at: 2026-04-13T00:03:20Z
content_hash: 506c27a2bf8c38ad9704a90493ddeba62394c455fc34f8a15daf10f176f38332
definition:
  type EmailMessage struct {
      From        string
      To          []string
      CC          []string
      BCC         []string
      Subject     string
      BodyHTML    string
      BodyText    string
      Attachments []EmailAttachment
      Headers     map[string]string
  }

description:
  Mensaje de email listo para enviar. Inmutable — construir con EmailBuildHTML, EmailBuildText o EmailWithAttachment.

examples:
  ```go
  msg := EmailBuildHTML("alice@example.com", []string{"bob@example.com"}, "Hola", "<b>Hola Bob</b>")
  ```

notes:
  Tipo producto. `BodyHTML` y `BodyText` son opcionales pero al menos uno debe estar presente para que el mensaje sea valido. `Attachments` puede ser nil. `Headers` permite agregar cabeceras MIME custom como `X-Mailer`.

code:
  package infra

  // EmailAttachment represents a file attached to an email.
  type EmailAttachment struct {
  	Filename    string // nombre del archivo (ej: "report.pdf")
  	ContentType string // MIME type (ej: "application/pdf", "image/png")
  	Data        []byte // contenido binario del adjunto
  }

  // EmailMessage is an immutable email ready to send.
  // Build with EmailBuildHTML, EmailBuildText, or EmailWithAttachment.
  type EmailMessage struct {
  	From        string            // direccion del remitente (ej: "Alice <alice@example.com>")
  	To          []string          // destinatarios principales
  	CC          []string          // copia (puede ser nil)
  	BCC         []string          // copia oculta (puede ser nil)
  	Subject     string            // asunto del mensaje
  	BodyHTML    string            // cuerpo HTML (puede ser vacio si BodyText esta presente)
  	BodyText    string            // cuerpo texto plano (puede ser vacio si BodyHTML esta presente)
  	Attachments []EmailAttachment // adjuntos (puede ser nil)
  	Headers     map[string]string // headers MIME adicionales (ej: "X-Mailer": "fn_registry")
  }

  // SMTPConfig holds connection parameters for an SMTP server.
  // TLSMode controls the encryption strategy:
  //
  //	"tls"      — TLS directo (port 465)
  //	"starttls" — STARTTLS upgrade (port 587)
  //	""         — sin cifrado (port 25)
  type SMTPConfig struct {
  	Host     string // hostname del servidor SMTP (ej: "smtp.gmail.com")
  	Port     int    // puerto (465, 587, 25)
  	Username string // usuario/email de autenticacion
  	Password string // contrasena o app password
  	TLSMode  string // "tls", "starttls" o "" (sin cifrado)
  }


id: HTTPError_go_infra
name: HTTPError
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type HTTPError struct {
  Status int
  Code string
  Message string
  }

tags: ["http","error","server","json","infra"]
uses_types: []
file_path: functions/infra/http_error.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 6effae339f71fcbee6b0eafe11ac33ae75cb066a16e4190d0a4e323f9ad8ebbe
description:
  Error HTTP estructurado para respuestas JSON. Combina status code HTTP, codigo de error legible por maquina y mensaje para el usuario.

examples:
  ```go
  err := HTTPError{
      Status:  400,
      Code:    "invalid_input",
      Message: "el campo email es requerido",
  }
  HTTPErrorResponse(w, err)
  ```

notes:
  Tipo producto — los tres campos son complementarios. Status es el codigo HTTP numerico que se usa en la respuesta. Code es un identificador maquina (snake_case) para el cliente. Message es texto libre legible por humanos. Se serializa a JSON con HTTPErrorResponse.

code:
  package infra

  // HTTPError representa un error HTTP estructurado para respuestas JSON.
  type HTTPError struct {
  	Status  int    // codigo HTTP (ej: 400, 404, 500)
  	Code    string // codigo de error legible por maquina (ej: "invalid_input")
  	Message string // mensaje de error legible por humano
  }


id: Middleware_go_infra
name: Middleware
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition: type Middleware func(http.Handler) http.Handler
description: Funcion que envuelve un http.Handler para añadir comportamiento (logging, auth, CORS, etc.).
tags: ["http","middleware","server","handler","infra"]
uses_types: []
file_path: functions/infra/http_middleware.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 7e037021df9fd4c2d42f2b92fd55f905de99275e8ceafee3982f878c878133f6
examples:
  ```go
  // Middleware que añade un header a cada respuesta
  func myMiddleware(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
          w.Header().Set("X-Custom", "value")
          next.ServeHTTP(w, r)
      })
  }
  ```

notes:
  Tipo funcion — aplica el patron decorator sobre http.Handler. Se compone con HTTPMiddlewareChain para apilar comportamiento en orden. Cada Middleware recibe el siguiente handler y retorna un nuevo handler que puede modificar request y response.

code:
  package infra

  import "net/http"

  // Middleware es una funcion que envuelve un http.Handler para añadir comportamiento.
  type Middleware func(http.Handler) http.Handler


id: Route_go_infra
name: Route
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type Route struct {
  Method string
  Path string
  Handler http.Handler
  }

tags: ["http","route","server","router","infra"]
uses_types: []
file_path: functions/infra/http_route.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 619156d6679d718fcf9b3b41c14e67ecc98934deac61991980d99712c5ff1c14
description:
  Define una ruta HTTP con metodo, path y handler. Se usa con HTTPRouter para registrar rutas en un ServeMux.

examples:
  ```go
  routes := []Route{
      {Method: "GET", Path: "/api/health", Handler: http.HandlerFunc(healthHandler)},
      {Method: "POST", Path: "/api/users", Handler: http.HandlerFunc(createUserHandler)},
  }
  mux := HTTPRouter(routes)
  ```

notes:
  Tipo producto — los tres campos son obligatorios. Method debe ser un verbo HTTP valido en mayusculas. Path sigue la sintaxis del ServeMux de Go 1.22+ (soporta parametros como {id}). Handler puede ser cualquier http.Handler o http.HandlerFunc.

code:
  package infra

  import "net/http"

  // Route define una ruta HTTP con metodo, path y handler.
  type Route struct {
  	Method  string       // metodo HTTP (GET, POST, PUT, DELETE, PATCH, etc.)
  	Path    string       // path de la ruta (ej: /api/users/{id})
  	Handler http.Handler // handler que procesa la peticion
  }


id: image_info_go_infra
name: image_info
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Información básica de una imagen Docker local: ID, repositorio, tag, tamaño, fecha.
tags: ["docker","image","infra"]
uses_types: []
file_path: functions/infra/image_info.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
notes: Tipo product que modela la salida de `docker images`. Los campos mapean al formato JSON de Docker.
content_hash: f58304810989e8d4277cc3af16db494f73d5b0d6ffa620d21d31b249825e6245
definition:
  type ImageInfo struct {
    ID         string
    Repository string
    Tag        string
    Size       string
    Created    string
  }

examples:
  ```go
  img := ImageInfo{
      ID:         "sha256:abc123",
      Repository: "nginx",
      Tag:        "latest",
      Size:       "142MB",
      Created:    "2 weeks ago",
  }
  ```

code:
  package infra

  // ImageInfo representa la información básica de una imagen Docker local.
  type ImageInfo struct {
  	ID         string // ID corto de la imagen
  	Repository string // Nombre del repositorio
  	Tag        string // Tag de la imagen
  	Size       string // Tamaño legible (ej: "142MB")
  	Created    string // Fecha de creación
  }


id: job_go_infra
name: job
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["job","queue","async","background","sqlite","infra"]
uses_types: ["job_status_go_infra"]
file_path: functions/infra/job_queue_types.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 1b665274dfb29db225c221d8a8bc483b02258509e429c1085364b86be4d92610
definition:
  type Job struct {
      ID          string
      Type        string
      Payload     string
      Status      JobStatus
      Priority    int
      Attempts    int
      MaxAttempts int
      ScheduledAt time.Time
      StartedAt   *time.Time
      CompletedAt *time.Time
      Result      *string
      Error       *string
      CreatedAt   time.Time
  }

description:
  Unidad de trabajo asincrono almacenada en SQLite. Payload es un JSON string. Status evoluciona de pending a running y luego a completed, failed o dead.

notes:
  Tipo producto. Los campos `StartedAt`, `CompletedAt`, `Result` y `Error` son punteros — son nil hasta que el job alcanza el estado correspondiente. `Payload` es siempre un JSON string (minimo "{}").

code:
  package infra

  import (
  	"database/sql"
  	"time"
  )

  // JobStatus represents the lifecycle state of a job.
  type JobStatus string

  const (
  	JobStatusPending   JobStatus = "pending"
  	JobStatusRunning   JobStatus = "running"
  	JobStatusCompleted JobStatus = "completed"
  	JobStatusFailed    JobStatus = "failed"
  	JobStatusDead      JobStatus = "dead"
  )

  // Job represents a unit of asynchronous work stored in SQLite.
  type Job struct {
  	ID          string
  	Type        string
  	Payload     string    // JSON string
  	Status      JobStatus
  	Priority    int
  	Attempts    int
  	MaxAttempts int
  	ScheduledAt time.Time
  	StartedAt   *time.Time
  	CompletedAt *time.Time
  	Result      *string
  	Error       *string
  	CreatedAt   time.Time
  }

  // JobQueue wraps a *sql.DB and the jobs table name.
  type JobQueue struct {
  	DB        *sql.DB
  	TableName string
  }

  // JobHandler is the function signature for processing a job.
  // Return a non-nil error to trigger retry / fail logic.
  type JobHandler func(job Job) error

  // EnqueueOption configures optional behaviour of JobEnqueue.
  type EnqueueOption func(*enqueueConfig)

  type enqueueConfig struct {
  	priority    int
  	scheduledAt time.Time
  	maxAttempts int
  }

  // WithPriority sets the job priority (higher = processed first).
  func WithPriority(p int) EnqueueOption {
  	return func(c *enqueueConfig) { c.priority = p }
  }

  // WithScheduledAt delays execution until the given time.
  func WithScheduledAt(t time.Time) EnqueueOption {
  	return func(c *enqueueConfig) { c.scheduledAt = t }
  }

  // WithMaxAttempts sets how many times the job may be attempted before dying.
  func WithMaxAttempts(n int) EnqueueOption {
  	return func(c *enqueueConfig) { c.maxAttempts = n }
  }

  // WorkerOption configures optional behaviour of JobWorker / JobWorkerPool.
  type WorkerOption func(*workerConfig)

  type workerConfig struct {
  	pollInterval time.Duration
  	jobTypes     []string
  }

  // WithPollInterval sets how long the worker sleeps between dequeue attempts.
  func WithPollInterval(d time.Duration) WorkerOption {
  	return func(c *workerConfig) { c.pollInterval = d }
  }

  // WithJobTypes restricts the worker to the given job types.
  // An empty list means all types.
  func WithJobTypes(types ...string) WorkerOption {
  	return func(c *workerConfig) { c.jobTypes = types }
  }


id: job_queue_go_infra
name: job_queue
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type JobQueue struct {
  DB *sql.DB
  TableName string
  }

tags: ["job","queue","sqlite","infra","async"]
uses_types: []
file_path: functions/infra/job_queue_types.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: b9b38f5445d3fe7286564dc06117976264b0c2f11e7653908590d0a9db461bf5
description:
  Handle para una cola de jobs SQLite. Wrappea un *sql.DB y el nombre de la tabla de jobs. Se crea con JobQueueCreate.

notes:
  Tipo producto minimo — todas las funciones del subsistema de jobs lo reciben como primer argumento. TableName permite tener multiples colas en la misma DB. Se crea con `JobQueueCreate`.

code:
  package infra

  import (
  	"database/sql"
  	"time"
  )

  // JobStatus represents the lifecycle state of a job.
  type JobStatus string

  const (
  	JobStatusPending   JobStatus = "pending"
  	JobStatusRunning   JobStatus = "running"
  	JobStatusCompleted JobStatus = "completed"
  	JobStatusFailed    JobStatus = "failed"
  	JobStatusDead      JobStatus = "dead"
  )

  // Job represents a unit of asynchronous work stored in SQLite.
  type Job struct {
  	ID          string
  	Type        string
  	Payload     string    // JSON string
  	Status      JobStatus
  	Priority    int
  	Attempts    int
  	MaxAttempts int
  	ScheduledAt time.Time
  	StartedAt   *time.Time
  	CompletedAt *time.Time
  	Result      *string
  	Error       *string
  	CreatedAt   time.Time
  }

  // JobQueue wraps a *sql.DB and the jobs table name.
  type JobQueue struct {
  	DB        *sql.DB
  	TableName string
  }

  // JobHandler is the function signature for processing a job.
  // Return a non-nil error to trigger retry / fail logic.
  type JobHandler func(job Job) error

  // EnqueueOption configures optional behaviour of JobEnqueue.
  type EnqueueOption func(*enqueueConfig)

  type enqueueConfig struct {
  	priority    int
  	scheduledAt time.Time
  	maxAttempts int
  }

  // WithPriority sets the job priority (higher = processed first).
  func WithPriority(p int) EnqueueOption {
  	return func(c *enqueueConfig) { c.priority = p }
  }

  // WithScheduledAt delays execution until the given time.
  func WithScheduledAt(t time.Time) EnqueueOption {
  	return func(c *enqueueConfig) { c.scheduledAt = t }
  }

  // WithMaxAttempts sets how many times the job may be attempted before dying.
  func WithMaxAttempts(n int) EnqueueOption {
  	return func(c *enqueueConfig) { c.maxAttempts = n }
  }

  // WorkerOption configures optional behaviour of JobWorker / JobWorkerPool.
  type WorkerOption func(*workerConfig)

  type workerConfig struct {
  	pollInterval time.Duration
  	jobTypes     []string
  }

  // WithPollInterval sets how long the worker sleeps between dequeue attempts.
  func WithPollInterval(d time.Duration) WorkerOption {
  	return func(c *workerConfig) { c.pollInterval = d }
  }

  // WithJobTypes restricts the worker to the given job types.
  // An empty list means all types.
  func WithJobTypes(types ...string) WorkerOption {
  	return func(c *workerConfig) { c.jobTypes = types }
  }


id: job_status_go_infra
name: job_status
lang: go
domain: infra
version: 1.0.0
algebraic: sum
tags: ["job","status","queue","async","infra"]
uses_types: []
file_path: functions/infra/job_queue_types.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: aad4ec79811ae4dd2606009d8871aef7c57cb655c2da98a6d3e9922e866d7434
definition:
  type JobStatus string

  const (
      JobStatusPending   JobStatus = "pending"
      JobStatusRunning   JobStatus = "running"
      JobStatusCompleted JobStatus = "completed"
      JobStatusFailed    JobStatus = "failed"
      JobStatusDead      JobStatus = "dead"
  )

description:
  Estado del ciclo de vida de un job. Tipo suma con cinco variantes: pending, running, completed, failed, dead.

notes:
  Tipo suma. Ciclo de vida normal: pending → running → completed. En error: pending → running → failed (reintentos posibles) → dead (sin mas intentos). Un job con `attempts >= max_attempts` pasa directamente a dead.

code:
  package infra

  import (
  	"database/sql"
  	"time"
  )

  // JobStatus represents the lifecycle state of a job.
  type JobStatus string

  const (
  	JobStatusPending   JobStatus = "pending"
  	JobStatusRunning   JobStatus = "running"
  	JobStatusCompleted JobStatus = "completed"
  	JobStatusFailed    JobStatus = "failed"
  	JobStatusDead      JobStatus = "dead"
  )

  // Job represents a unit of asynchronous work stored in SQLite.
  type Job struct {
  	ID          string
  	Type        string
  	Payload     string    // JSON string
  	Status      JobStatus
  	Priority    int
  	Attempts    int
  	MaxAttempts int
  	ScheduledAt time.Time
  	StartedAt   *time.Time
  	CompletedAt *time.Time
  	Result      *string
  	Error       *string
  	CreatedAt   time.Time
  }

  // JobQueue wraps a *sql.DB and the jobs table name.
  type JobQueue struct {
  	DB        *sql.DB
  	TableName string
  }

  // JobHandler is the function signature for processing a job.
  // Return a non-nil error to trigger retry / fail logic.
  type JobHandler func(job Job) error

  // EnqueueOption configures optional behaviour of JobEnqueue.
  type EnqueueOption func(*enqueueConfig)

  type enqueueConfig struct {
  	priority    int
  	scheduledAt time.Time
  	maxAttempts int
  }

  // WithPriority sets the job priority (higher = processed first).
  func WithPriority(p int) EnqueueOption {
  	return func(c *enqueueConfig) { c.priority = p }
  }

  // WithScheduledAt delays execution until the given time.
  func WithScheduledAt(t time.Time) EnqueueOption {
  	return func(c *enqueueConfig) { c.scheduledAt = t }
  }

  // WithMaxAttempts sets how many times the job may be attempted before dying.
  func WithMaxAttempts(n int) EnqueueOption {
  	return func(c *enqueueConfig) { c.maxAttempts = n }
  }

  // WorkerOption configures optional behaviour of JobWorker / JobWorkerPool.
  type WorkerOption func(*workerConfig)

  type workerConfig struct {
  	pollInterval time.Duration
  	jobTypes     []string
  }

  // WithPollInterval sets how long the worker sleeps between dequeue attempts.
  func WithPollInterval(d time.Duration) WorkerOption {
  	return func(c *workerConfig) { c.pollInterval = d }
  }

  // WithJobTypes restricts the worker to the given job types.
  // An empty list means all types.
  func WithJobTypes(types ...string) WorkerOption {
  	return func(c *workerConfig) { c.jobTypes = types }
  }


id: MetabaseClient_go_infra
name: MetabaseClient
lang: go
domain: infra
version: 1.0.0
algebraic: product
definition:
  type MetabaseClient struct {
  BaseURL string
  Token string
  }

tags: ["metabase","api","client","infra"]
uses_types: []
file_path: functions/infra/metabase_client.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: e95a90c25f7e3a2522415d052fc07c3b9e280fde42d80064768f3014450908f5
description:
  Cliente para la API REST de Metabase. Contiene la URL base de la instancia y el token de autenticacion (session token o API key).

notes:
  Tipo producto con dos campos obligatorios:
  - `BaseURL`: URL base de la instancia Metabase sin trailing slash (ej: `http://localhost:3000`)
  - `Token`: token de sesion obtenido con `MetabaseAuth()` o una API key creada en el admin UI de Metabase

  El token se envia como header `X-Metabase-Session` en session tokens o `x-api-key` en API keys. Las funciones del registry usan `X-Metabase-Session` por defecto.

code:
  package infra

  // MetabaseClient holds the connection details for a Metabase instance API.
  type MetabaseClient struct {
  	BaseURL string // e.g. "http://localhost:3000"
  	Token   string // session token or API key
  }


id: Migration_go_infra
name: Migration
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["migration","database","sql","schema","sqlite"]
uses_types: []
file_path: functions/infra/migration.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: f5ec74ec4698b63bd59c805c65a8e2b25c799c603faae747cc98b4f8b8699cb9
definition:
  type Migration struct {
      Version   int
      Name      string
      UpSQL     string
      DownSQL   string
      AppliedAt time.Time
  }

description:
  Migracion SQL parseada desde un archivo .sql con marcadores -- +up / -- +down. Version es el numero secuencial, AppliedAt es zero value si pendiente.

notes:
  Tipo producto — todos los campos siempre presentes. `AppliedAt` es `time.Time{}` (zero value) si la migracion aun no fue aplicada. `UpSQL` y `DownSQL` son el contenido de los bloques delimitados por `-- +up` y `-- +down` en el archivo .sql.

code:
  package infra

  import "time"

  // Migration represents a migration parsed from a .sql file.
  type Migration struct {
  	Version   int       // sequential number (1, 2, 3...)
  	Name      string    // descriptive name (create_entities, add_status_column)
  	UpSQL     string    // SQL block to apply the migration
  	DownSQL   string    // SQL block to revert the migration
  	AppliedAt time.Time // zero value if not yet applied
  }

  // MigrationStatus represents the state of a migration relative to a database.
  type MigrationStatus struct {
  	Version   int       // sequential number
  	Name      string    // descriptive name
  	Applied   bool      // true if already applied in the database
  	AppliedAt time.Time // when it was applied (zero value if pending)
  }


id: MigrationStatus_go_infra
name: MigrationStatus
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["migration","database","sql","schema","sqlite","status"]
uses_types: []
file_path: functions/infra/migration.go
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 51933c3681cc6fae10d99af43b30b08409320a1bde0fe79a563aa1d8ea90b6ad
definition:
  type MigrationStatus struct {
      Version   int
      Name      string
      Applied   bool
      AppliedAt time.Time
  }

description:
  Estado de una migracion respecto a una base de datos concreta. Applied=true si ya fue ejecutada, AppliedAt indica cuando.

notes:
  Tipo producto. `Applied` es `false` y `AppliedAt` es zero value para migraciones pendientes. Se usa como resultado de `migration_status` para cruzar archivos en disco con registros en `_migrations`.

code:
  package infra

  import "time"

  // Migration represents a migration parsed from a .sql file.
  type Migration struct {
  	Version   int       // sequential number (1, 2, 3...)
  	Name      string    // descriptive name (create_entities, add_status_column)
  	UpSQL     string    // SQL block to apply the migration
  	DownSQL   string    // SQL block to revert the migration
  	AppliedAt time.Time // zero value if not yet applied
  }

  // MigrationStatus represents the state of a migration relative to a database.
  type MigrationStatus struct {
  	Version   int       // sequential number
  	Name      string    // descriptive name
  	Applied   bool      // true if already applied in the database
  	AppliedAt time.Time // when it was applied (zero value if pending)
  }


id: NordVPNStatus_go_infra
name: NordVPNStatus
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["vpn","nordvpn","status","infra"]
uses_types: []
file_path: functions/infra/parse_nordvpn_status.go
created_at: 2026-04-01T14:14:49Z
updated_at: 2026-04-01T14:14:49Z
content_hash: 5f8a8c6a2e17490901f478cd8adb7761e688e9d0efea96a4e9ff1e63a3a1cb71
definition:
  type NordVPNStatus struct { Connected bool; Status string; Hostname string; IP string; Country string; City string; Technology string; Protocol string; Transfer string; Uptime string }

description:
  Estado parseado de nordvpn status. Contiene informacion de conexion, servidor, ubicacion y protocolo.

examples:
  ```go
  s := NordVPNStatus{
      Connected:  true,
      Status:     "Connected",
      Hostname:   "es42.nordvpn.com",
      IP:         "185.230.124.42",
      Country:    "Spain",
      City:       "Madrid",
      Technology: "NordLynx",
      Protocol:   "nordlynx",
  }
  ```

notes:
  Producido por ParseNordVPNStatus. Los campos corresponden a las claves de la salida de `nordvpn status`. Transfer y Uptime son strings sin parsear — incluir parseo numerico si se necesita.

code:
  package infra

  import (
  	"regexp"
  	"strings"
  )

  // NordVPNStatus representa el estado parseado de nordvpn status.
  type NordVPNStatus struct {
  	Connected  bool   // true si hay conexion activa
  	Status     string // "Connected" o "Disconnected"
  	Hostname   string // ej: "es42.nordvpn.com"
  	IP         string // IP del servidor VPN
  	Country    string // ej: "Spain"
  	City       string // ej: "Madrid"
  	Technology string // ej: "NordLynx"
  	Protocol   string // ej: "nordlynx"
  	Transfer   string // ej: "1.2 MiB received, 500 KiB sent"
  	Uptime     string // ej: "5 minutes 32 seconds"
  }

  // ansiRegexp elimina codigos de escape ANSI.
  var ansiRegexp = regexp.MustCompile(`\x1b\[[0-9;]*m`)

  // ParseNordVPNStatus parsea la salida de texto de `nordvpn status`
  // a un struct tipado. Funcion pura — no ejecuta comandos.
  func ParseNordVPNStatus(output string) NordVPNStatus {
  	var s NordVPNStatus

  	lines := strings.Split(output, "\n")
  	for _, line := range lines {
  		line = ansiRegexp.ReplaceAllString(line, "")
  		line = strings.TrimSpace(line)
  		line = strings.TrimLeft(line, "- ")

  		idx := strings.Index(line, ":")
  		if idx < 0 {
  			continue
  		}

  		key := strings.ToLower(strings.TrimSpace(line[:idx]))
  		val := strings.TrimSpace(line[idx+1:])

  		switch key {
  		case "status":
  			s.Status = val
  			s.Connected = strings.EqualFold(val, "connected")
  		case "hostname", "server":
  			s.Hostname = val
  		case "ip":
  			s.IP = val
  		case "country":
  			s.Country = val
  		case "city":
  			s.City = val
  		case "current technology":
  			s.Technology = val
  		case "current protocol":
  			s.Protocol = val
  		case "transfer":
  			s.Transfer = val
  		case "uptime":
  			s.Uptime = val
  		}
  	}

  	return s
  }


id: process_handle_go_infra
name: process_handle
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["process","subprocess","handle","infra","exec"]
uses_types: []
file_path: functions/infra/process_handle.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: 0f8f4b74feca5ee45d41334c54991b0dc0c9ca7a79f36548de33bdfa502f3ebc
definition:
  type ProcessHandle struct {
      Cmd       *exec.Cmd
      Pid       int
      StartTime time.Time
      Dir       string
      stdout    io.ReadCloser
      stderr    io.ReadCloser
  }

description:
  Handle de un subproceso en ejecucion. Contiene el comando, PID, tiempo de inicio, directorio de trabajo y los pipes de stdout/stderr (privados, leidos internamente por ProcessWait).

notes:
  Tipo producto. Los campos stdout y stderr son privados para evitar lecturas concurrentes externas — ProcessWait los consume internamente. Cmd.SysProcAttr.Setpgid=true garantiza que ProcessKill puede matar el process group completo usando -Pid.

code:
  package infra

  import (
  	"bytes"
  	"os/exec"
  	"time"
  )

  // ProcessHandle represents a running subprocess with output buffers.
  type ProcessHandle struct {
  	Cmd       *exec.Cmd
  	Pid       int
  	StartTime time.Time
  	Dir       string
  	stdout    *bytes.Buffer
  	stderr    *bytes.Buffer
  }

  // ProcessResult contains the outcome of a completed subprocess.
  type ProcessResult struct {
  	ExitCode   int
  	Stdout     string
  	Stderr     string
  	DurationMs int64
  	Killed     bool
  }


id: process_result_go_infra
name: process_result
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["process","subprocess","result","exit","infra","exec"]
uses_types: []
file_path: functions/infra/process_handle.go
created_at: 2026-04-12T11:03:11Z
updated_at: 2026-04-12T11:03:11Z
content_hash: 1adb313383b5e41950a34d47adc360d299c4913b126d08dd8ec6d470bfecb430
definition:
  type ProcessResult struct {
      ExitCode   int
      Stdout     string
      Stderr     string
      DurationMs int64
      Killed     bool
  }

description:
  Resultado de un subproceso completado. Contiene codigo de salida, salida estandar y de error, duracion en milisegundos, y un flag que indica si fue terminado por timeout.

notes:
  Tipo producto — todos los campos siempre presentes. Killed=true indica que ProcessWait agoto el timeout y llamo a ProcessKill; en ese caso ExitCode suele ser -1 o el codigo de SIGKILL segun el OS. DurationMs incluye el tiempo total desde ProcessSpawn hasta que Wait() retorno.

code:
  package infra

  import (
  	"bytes"
  	"os/exec"
  	"time"
  )

  // ProcessHandle represents a running subprocess with output buffers.
  type ProcessHandle struct {
  	Cmd       *exec.Cmd
  	Pid       int
  	StartTime time.Time
  	Dir       string
  	stdout    *bytes.Buffer
  	stderr    *bytes.Buffer
  }

  // ProcessResult contains the outcome of a completed subprocess.
  type ProcessResult struct {
  	ExitCode   int
  	Stdout     string
  	Stderr     string
  	DurationMs int64
  	Killed     bool
  }


id: SMTPConfig_go_infra
name: SMTPConfig
lang: go
domain: infra
version: 1.0.0
algebraic: product
tags: ["email","smtp","config","tls"]
uses_types: []
file_path: functions/infra/email_types.go
created_at: 2026-04-13T00:03:20Z
updated_at: 2026-04-13T00:03:20Z
content_hash: 4386e327933002fca2b3f7a0e1530ca7a837d310332c5f8057bbf2546803fcd4
definition:
  type SMTPConfig struct {
      Host     string
      Port     int
      Username string
      Password string
      TLSMode  string
  }

description:
  Parametros de conexion a un servidor SMTP. TLSMode controla el cifrado: 'tls' (port 465), 'starttls' (port 587) o '' sin cifrado (port 25).

examples:
  ```go
  cfg := SMTPConfig{
      Host:     "smtp.gmail.com",
      Port:     587,
      Username: "user@gmail.com",
      Password: "app-password",
      TLSMode:  "starttls",
  }
  ```

notes:
  Tipo producto. `TLSMode` debe ser `"tls"`, `"starttls"` o `""`. Por convencion: port 465 usa TLS directo, 587 usa STARTTLS, 25 sin cifrado. Para Gmail y proveedores modernos usar `"starttls"` en port 587 o `"tls"` en 465.

code:
  package infra

  // EmailAttachment represents a file attached to an email.
  type EmailAttachment struct {
  	Filename    string // nombre del archivo (ej: "report.pdf")
  	ContentType string // MIME type (ej: "application/pdf", "image/png")
  	Data        []byte // contenido binario del adjunto
  }

  // EmailMessage is an immutable email ready to send.
  // Build with EmailBuildHTML, EmailBuildText, or EmailWithAttachment.
  type EmailMessage struct {
  	From        string            // direccion del remitente (ej: "Alice <alice@example.com>")
  	To          []string          // destinatarios principales
  	CC          []string          // copia (puede ser nil)
  	BCC         []string          // copia oculta (puede ser nil)
  	Subject     string            // asunto del mensaje
  	BodyHTML    string            // cuerpo HTML (puede ser vacio si BodyText esta presente)
  	BodyText    string            // cuerpo texto plano (puede ser vacio si BodyHTML esta presente)
  	Attachments []EmailAttachment // adjuntos (puede ser nil)
  	Headers     map[string]string // headers MIME adicionales (ej: "X-Mailer": "fn_registry")
  }

  // SMTPConfig holds connection parameters for an SMTP server.
  // TLSMode controls the encryption strategy:
  //
  //	"tls"      — TLS directo (port 465)
  //	"starttls" — STARTTLS upgrade (port 587)
  //	""         — sin cifrado (port 25)
  type SMTPConfig struct {
  	Host     string // hostname del servidor SMTP (ej: "smtp.gmail.com")
  	Port     int    // puerto (465, 587, 25)
  	Username string // usuario/email de autenticacion
  	Password string // contrasena o app password
  	TLSMode  string // "tls", "starttls" o "" (sin cifrado)
  }


id: ssh_config_entry_go_infra
name: ssh_config_entry
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Bloque Host de ~/.ssh/config. Contiene alias, hostname, usuario, puerto, clave y opciones extra.
tags: ["ssh","config","remote","infra"]
uses_types: []
file_path: functions/infra/ssh_config_entry.go
created_at: 2026-04-08T21:06:03Z
updated_at: 2026-04-08T21:06:03Z
content_hash: 0a7b82bae8976f9a9c78dc1f5f1fbbad7a0c71b930c2ced1f05e3ef50c88863d
definition:
  type SSHConfigEntry struct {
      Alias        string
      HostName     string
      User         string
      Port         int
      IdentityFile string
      Options      map[string]string
  }

notes:
  Tipo producto — modela un bloque `Host` del archivo `~/.ssh/config`. Port=0 significa que no se especifica (el SSH client usa 22 por defecto). Options contiene pares clave-valor para directivas SSH adicionales como ForwardAgent, ProxyJump, ServerAliveInterval, etc.

  Incluye metodo `ToSSHConn()` para convertir a `SSHConn` y poder usar las funciones SSH operativas del registry (ssh_exec, ssh_check, etc.).

code:
  package infra

  // SSHConfigEntry representa un bloque Host en ~/.ssh/config.
  type SSHConfigEntry struct {
  	Alias        string            // Nombre del host (lo que va despues de "Host")
  	HostName     string            // IP o hostname real del servidor
  	User         string            // Usuario remoto
  	Port         int               // Puerto SSH (0 = no especificado, usa default 22)
  	IdentityFile string            // Ruta a clave privada
  	Options      map[string]string // Opciones SSH adicionales (ForwardAgent, ProxyJump, etc.)
  }

  // ToSSHConn convierte un SSHConfigEntry a SSHConn para usar con las funciones SSH del registry.
  func (e SSHConfigEntry) ToSSHConn() SSHConn {
  	return SSHConn{
  		Host:    e.HostName,
  		Port:    e.Port,
  		User:    e.User,
  		KeyPath: e.IdentityFile,
  	}
  }


id: ssh_conn_go_infra
name: ssh_conn
lang: go
domain: infra
version: 1.0.0
algebraic: product
description: Parametros de conexion SSH reutilizables. Contiene host, puerto, usuario y ruta a clave privada.
tags: ["ssh","connection","remote","infra"]
uses_types: []
file_path: functions/infra/ssh_conn.go
created_at: 2026-04-02T13:39:01Z
updated_at: 2026-04-02T13:39:01Z
content_hash: e4ee2945171d2a99273d2bf6606a08026e33cbd0b1f4439548ae022e252fa0a9
definition:
  type SSHConn struct {
      Host    string // Hostname o IP del servidor remoto
      Port    int    // Puerto SSH (0 usa el default 22)
      User    string // Usuario remoto
      KeyPath string // Ruta a clave privada (vacio usa ssh-agent o default)
  }

notes:
  Tipo producto — todos los campos siempre presentes. Port=0 se interpreta como puerto 22 por defecto. KeyPath vacio delega la autenticacion a ssh-agent o la clave default (~/.ssh/id_rsa). Incluye metodos helper (sshArgs, scpArgs, destination) que las funciones SSH del registry consumen internamente.

code:
  package infra

  import "fmt"

  // SSHConn parametros de conexion SSH reutilizables.
  type SSHConn struct {
  	Host    string // Hostname o IP del servidor remoto
  	Port    int    // Puerto SSH (0 usa el default 22)
  	User    string // Usuario remoto
  	KeyPath string // Ruta a clave privada (vacio usa ssh-agent o default)
  }

  // sshArgs construye los argumentos comunes de ssh/scp a partir de SSHConn.
  func (c SSHConn) sshArgs() []string {
  	var args []string
  	port := c.Port
  	if port == 0 {
  		port = 22
  	}
  	args = append(args, "-o", "StrictHostKeyChecking=accept-new")
  	args = append(args, "-p", fmt.Sprintf("%d", port))
  	if c.KeyPath != "" {
  		args = append(args, "-i", c.KeyPath)
  	}
  	return args
  }

  // scpArgs construye los argumentos comunes de scp a partir de SSHConn.
  func (c SSHConn) scpArgs() []string {
  	var args []string
  	port := c.Port
  	if port == 0 {
  		port = 22
  	}
  	args = append(args, "-o", "StrictHostKeyChecking=accept-new")
  	args = append(args, "-P", fmt.Sprintf("%d", port))
  	if c.KeyPath != "" {
  		args = append(args, "-i", c.KeyPath)
  	}
  	return args
  }

  // destination retorna user@host.
  func (c SSHConn) destination() string {
  	if c.User != "" {
  		return c.User + "@" + c.Host
  	}
  	return c.Host
  }


id: cmd_result_go_shell
name: cmd_result
lang: go
domain: shell
version: 1.0.0
algebraic: product
definition:
  type CmdResult struct {
  Stdout string
  Stderr string
  ExitCode int
  }

description: Resultado de la ejecucion de un comando del sistema con stdout, stderr y codigo de salida.
tags: ["shell","command","process","result"]
uses_types: []
file_path: functions/shell/cmd_result.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
notes: Tipo producto. Metodos Output(), Lines(), Success(), ToResult() disponibles sobre la instancia.
content_hash: 490ac14f0bd4cf39e46514120bb444e51a24ec6fa981c709655fd01ca993f193
code:
  package shell

  // CmdResult almacena el resultado de ejecutar un comando del sistema.
  // Implementation: github.com/lucasdataproyects/devfactory/shell
  type CmdResult struct {
  	Stdout   string
  	Stderr   string
  	ExitCode int
  }


id: base_model_go_tui
name: base_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
definition: type BaseModel struct { Width int; Height int; Styles Styles; Err error }
tags: ["tui","base","model","component"]
uses_types: ["styles_go_tui"]
file_path: functions/tui/base_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 4f5b78d0f96ce166d84bac22b92f37ef6e31469a3296fcee89fa6f713a14e0e2
description:
  Modelo base que provee dimensiones de terminal, estilos y manejo de errores comunes a todas las vistas TUI.

code:
  package tui

  // BaseModel provee funcionalidad comun a todas las vistas TUI:
  // dimensiones de terminal, estilos y manejo de errores.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type BaseModel struct {
  	Width  int
  	Height int
  	Styles Styles
  	Err    error
  }


id: confirm_model_go_tui
name: confirm_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
definition: type ConfirmModel struct { BaseModel; prompt string; selected bool; done bool }
description: Dialogo de confirmacion Si/No interactivo. Embeds BaseModel. Implementa tea.Model.
tags: ["tui","confirm","dialog","component","interactive"]
uses_types: ["base_model_go_tui"]
file_path: functions/tui/confirm_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: fe1838a87d9fd34620b294cf9eb14f740989cab5d4f19617aa1427fd328f7507
code:
  package tui

  // ConfirmModel es un dialogo de confirmacion Si/No interactivo.
  // Implementa tea.Model del framework Bubble Tea.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type ConfirmModel struct {
  	BaseModel
  	prompt   string
  	selected bool
  	done     bool
  }


id: filtered_list_model_go_tui
name: filtered_list_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
definition: type FilteredListModel struct { ListModel; query string; allItems []ListItem; placeholder string }
description: Lista con filtrado por texto en tiempo real. Embeds ListModel y añade busqueda interactiva.
tags: ["tui","list","filter","component","interactive"]
uses_types: ["list_model_go_tui","list_item_go_tui"]
file_path: functions/tui/filtered_list_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: f175db0fdb7ac4ee00a099d61f51cf3fb9d0cd3fbf8819888b79cb5417f2973b
code:
  package tui

  // FilteredListModel extiende ListModel con filtrado por texto en tiempo real.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type FilteredListModel struct {
  	ListModel
  	query       string
  	allItems    []ListItem
  	placeholder string
  }


id: list_item_go_tui
name: list_item
lang: go
domain: tui
version: 1.0.0
algebraic: product
definition: type ListItem struct { Title string; Description string; Value interface{} }
description: Item individual de una lista TUI con titulo, descripcion y valor arbitrario.
tags: ["tui","list","component"]
uses_types: []
file_path: functions/tui/list_item.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: a856fc54dcfeb26a4bcb06e758ae1192c3c395cf728620a40d95180aec150fc0
code:
  package tui

  // ListItem representa un item individual en una lista TUI.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type ListItem struct {
  	Title       string
  	Description string
  	Value       interface{}
  }


id: list_model_go_tui
name: list_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
tags: ["tui","list","component","interactive"]
uses_types: ["list_item_go_tui","styles_go_tui"]
file_path: functions/tui/list_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: a435ed9fe29c4472548effd2f519b759c1e4fbc136a5ac1347a1ebcc273db818
definition:
  type ListModel struct { items []ListItem; cursor int; selected map[int]struct{}; multi bool; styles Styles; height int; width int; offset int }

description:
  Componente lista seleccionable con cursor, scroll y seleccion simple o multiple. Implementa tea.Model.

code:
  package tui

  // ListModel es un componente lista seleccionable con cursor, scroll y seleccion.
  // Implementa tea.Model del framework Bubble Tea.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type ListModel struct {
  	items    []ListItem
  	cursor   int
  	selected map[int]struct{}
  	multi    bool
  	styles   Styles
  	height   int
  	width    int
  	offset   int
  }


id: multi_progress_model_go_tui
name: multi_progress_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
definition: type MultiProgressModel struct { bars []ProgressModel; styles Styles }
description: Gestor de multiples barras de progreso simultaneas. Implementa tea.Model.
tags: ["tui","progress","multi","component"]
uses_types: ["progress_model_go_tui","styles_go_tui"]
file_path: functions/tui/multi_progress_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 128e467fcf0d420b179e1e4ad79b4b7f5d66e8cb606912d1dbe2e314d73db5ff
code:
  package tui

  // MultiProgressModel gestiona multiples barras de progreso simultaneas.
  // Implementa tea.Model del framework Bubble Tea.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type MultiProgressModel struct {
  	bars   []ProgressModel
  	styles Styles
  }


id: progress_model_go_tui
name: progress_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
description: Barra de progreso con porcentaje, ETA y tiempo transcurrido. Implementa tea.Model.
tags: ["tui","progress","component","interactive"]
uses_types: ["styles_go_tui"]
file_path: functions/tui/progress_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 0d6c24f08e6e117922f36dd8a86aae3c3dcb16e02ed371cebace25bcf884598b
definition:
  type ProgressModel struct { progress progress.Model; current float64; total float64; label string; styles Styles; showPct bool; showETA bool; started time.Time }

code:
  package tui

  import (
  	"time"

  	"github.com/charmbracelet/bubbles/progress"
  )

  // ProgressModel es una barra de progreso con porcentaje, ETA y tiempo transcurrido.
  // Implementa tea.Model del framework Bubble Tea.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type ProgressModel struct {
  	progress progress.Model
  	current  float64
  	total    float64
  	label    string
  	styles   Styles
  	showPct  bool
  	showETA  bool
  	started  time.Time
  }


id: spinner_model_go_tui
name: spinner_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
description: Indicador de carga animado con mensaje personalizable. Implementa tea.Model.
tags: ["tui","spinner","loading","component"]
uses_types: []
file_path: functions/tui/spinner_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: ccd5eabc0e52ad5d3b2d2d0f0871daf8610f6e5cbd98c7e27f37d251e2969d43
definition:
  type SpinnerModel struct { spinner spinner.Model; message string; style lipgloss.Style; active bool }

code:
  package tui

  import (
  	"github.com/charmbracelet/bubbles/spinner"
  	"github.com/charmbracelet/lipgloss"
  )

  // SpinnerModel es un indicador de carga animado con mensaje personalizable.
  // Implementa tea.Model del framework Bubble Tea.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type SpinnerModel struct {
  	spinner spinner.Model
  	message string
  	style   lipgloss.Style
  	active  bool
  }


id: spinner_with_timeout_model_go_tui
name: spinner_with_timeout_model
lang: go
domain: tui
version: 1.0.0
algebraic: product
description: Spinner que se auto-detiene tras un timeout configurable. Embeds SpinnerModel.
tags: ["tui","spinner","timeout","component"]
uses_types: ["spinner_model_go_tui"]
file_path: functions/tui/spinner_with_timeout_model.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 4ef161de02fc5703fbe927151482f498d3623617ae300a7c769fc9f9cc78c9a4
definition:
  type SpinnerWithTimeoutModel struct { SpinnerModel; timeout time.Duration; started time.Time; finished bool }

code:
  package tui

  import "time"

  // SpinnerWithTimeoutModel es un spinner que se auto-detiene tras un timeout.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type SpinnerWithTimeoutModel struct {
  	SpinnerModel
  	timeout  time.Duration
  	started  time.Time
  	finished bool
  }


id: styles_go_tui
name: styles
lang: go
domain: tui
version: 1.0.0
algebraic: product
tags: ["tui","styles","lipgloss","theme"]
uses_types: ["theme_go_tui"]
file_path: functions/tui/styles.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: 2041e60650c74fd9f981f4b14838f2d26167c4a33ffbaafbd9aeeb02435fa997
definition:
  type Styles struct { Theme Theme; Title lipgloss.Style; Subtitle lipgloss.Style; Header lipgloss.Style; Label lipgloss.Style; Text lipgloss.Style; Muted lipgloss.Style; Code lipgloss.Style; Success lipgloss.Style; Error lipgloss.Style; Warning lipgloss.Style; Info lipgloss.Style; Border lipgloss.Style; Box lipgloss.Style; List lipgloss.Style; ListItem lipgloss.Style; Selected lipgloss.Style; Unselected lipgloss.Style; Button lipgloss.Style; ButtonActive lipgloss.Style; Input lipgloss.Style; Cursor lipgloss.Style; Container lipgloss.Style; Section lipgloss.Style; Divider lipgloss.Style; StatusBar lipgloss.Style }

description:
  Coleccion completa de estilos lipgloss pre-configurados para tipografia, estados, componentes y layout.

code:
  package tui

  import "github.com/charmbracelet/lipgloss"

  // Styles contiene todos los estilos lipgloss pre-configurados para una aplicacion TUI.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type Styles struct {
  	Theme        Theme
  	Title        lipgloss.Style
  	Subtitle     lipgloss.Style
  	Header       lipgloss.Style
  	Label        lipgloss.Style
  	Text         lipgloss.Style
  	Muted        lipgloss.Style
  	Code         lipgloss.Style
  	Success      lipgloss.Style
  	Error        lipgloss.Style
  	Warning      lipgloss.Style
  	Info         lipgloss.Style
  	Border       lipgloss.Style
  	Box          lipgloss.Style
  	List         lipgloss.Style
  	ListItem     lipgloss.Style
  	Selected     lipgloss.Style
  	Unselected   lipgloss.Style
  	Button       lipgloss.Style
  	ButtonActive lipgloss.Style
  	Input        lipgloss.Style
  	Cursor       lipgloss.Style
  	Container    lipgloss.Style
  	Section      lipgloss.Style
  	Divider      lipgloss.Style
  	StatusBar    lipgloss.Style
  }


id: theme_go_tui
name: theme
lang: go
domain: tui
version: 1.0.0
algebraic: product
description: Paleta de colores para terminal con 9 colores semanticos. Base del sistema de estilos.
tags: ["tui","theme","styles","colors"]
uses_types: []
file_path: functions/tui/theme.go
created_at: 2026-04-01T01:37:51Z
updated_at: 2026-04-01T01:37:51Z
content_hash: df55a02e9b36963af9125444666bcfa0bb211283f2be0ba4d8252b6f7ca73d93
definition:
  type Theme struct { Primary lipgloss.Color; Secondary lipgloss.Color; Success lipgloss.Color; Error lipgloss.Color; Warning lipgloss.Color; Info lipgloss.Color; Muted lipgloss.Color; Text lipgloss.Color; Border lipgloss.Color }

code:
  package tui

  import "github.com/charmbracelet/lipgloss"

  // Theme define una paleta de 9 colores semanticos para terminal.
  // Implementation: github.com/lucasdataproyects/devfactory/tui
  type Theme struct {
  	Primary   lipgloss.Color
  	Secondary lipgloss.Color
  	Success   lipgloss.Color
  	Error     lipgloss.Color
  	Warning   lipgloss.Color
  	Info      lipgloss.Color
  	Muted     lipgloss.Color
  	Text      lipgloss.Color
  	Border    lipgloss.Color
  }


id: GraphData_cpp_viz
name: GraphData
lang: cpp
domain: viz
version: 1.0.0
algebraic: product
tags: ["graph","network","visualization","gpu","force-layout","node","edge","imgui"]
uses_types: []
file_path: cpp/functions/viz/graph_types.h
created_at: 2026-04-07T22:00:02Z
updated_at: 2026-04-07T22:00:02Z
content_hash: d8a6fa4625361ba2e2fd95e9887acccd53eb8c9f559473295d2fbfafb4a17a11
definition:
  struct GraphNode {
      uint32_t id;
      float x, y;
      float vx, vy;
      float size;
      uint32_t color;
      const char* label;
      uint32_t community;
      float value;
      bool pinned;
  };

  struct GraphEdge {
      uint32_t source;
      uint32_t target;
      float weight;
      uint32_t color;
  };

  struct GraphData {
      GraphNode* nodes;
      int node_count;
      GraphEdge* edges;
      int edge_count;
      float min_x, min_y, max_x, max_y;
      void update_bounds();
      int find_node(uint32_t id) const;
  };

description:
  Tipos de datos base para el sistema de grafos GPU del registry. GraphNode modela un vertice con posicion, velocidad, apariencia y metadatos de layout. GraphEdge modela una arista con peso y color. GraphData es el contenedor principal que agrupa nodos y aristas con bounding box y metodos de consulta. Disenado para integrarse con force-directed layout y renderizado GPU via ImGui/ImPlot.

notes:
  - La memoria de `nodes` y `edges` es propiedad del caller. `GraphData` no hace `new`/`delete`.
  - `color` usa formato ABGR packed (compatible con ImGui `ImU32`): `0xAABBGGRR`.
  - Las aristas referencian por indice, no por id, para que el loop de simulacion sea cache-friendly.
  - `community` con valor 0 se interpreta como "sin grupo" — los colores de comunidad empiezan desde 1.

documentation:
  ## Structs

  ### GraphNode

  Vertice del grafo. Contiene todos los datos necesarios para el layout y el renderizado.

  | Campo | Tipo | Descripcion |
  |---|---|---|
  | `id` | `uint32_t` | Identificador unico del nodo |
  | `x, y` | `float` | Posicion en el espacio de layout |
  | `vx, vy` | `float` | Velocidad del nodo, usada por el algoritmo force-directed |
  | `size` | `float` | Radio visual en pixels (por defecto 4.0) |
  | `color` | `uint32_t` | Color en formato ABGR packed. 0 = usar paleta automatica por community |
  | `label` | `const char*` | Etiqueta visible. `nullptr` = sin etiqueta |
  | `community` | `uint32_t` | ID de grupo/cluster para auto-coloreo. 0 = sin grupo |
  | `value` | `float` | Metrica arbitraria (puede usarse para escalar el tamaño del nodo) |
  | `pinned` | `bool` | Si es `true`, el force layout no mueve este nodo |

  ### GraphEdge

  Arista del grafo. Referencia nodos por indice (no por id) para acceso O(1) en el loop de simulacion.

  | Campo | Tipo | Descripcion |
  |---|---|---|
  | `source` | `uint32_t` | Indice en `GraphData::nodes` del nodo origen |
  | `target` | `uint32_t` | Indice en `GraphData::nodes` del nodo destino |
  | `weight` | `float` | Peso de la arista. Afecta la fuerza de atraccion en el layout |
  | `color` | `uint32_t` | Color ABGR packed. 0 = gris por defecto |

  ### GraphData

  Contenedor principal. Posee los arrays de nodos y aristas (memoria gestionada externamente). Mantiene un bounding box actualizable para proyeccion de coordenadas a pantalla.

  | Campo/Metodo | Descripcion |
  |---|---|
  | `nodes` / `node_count` | Array de nodos y su longitud |
  | `edges` / `edge_count` | Array de aristas y su longitud |
  | `min_x, min_y, max_x, max_y` | Bounding box calculado sobre las posiciones actuales |
  | `update_bounds()` | Recalcula el bounding box iterando todos los nodos |
  | `find_node(id)` | Busqueda lineal por `GraphNode::id`. Retorna -1 si no existe |

  ## Helpers

  ```cpp
  // Crear un nodo con valores por defecto
  GraphNode n = graph_node(42, 100.0f, 200.0f);

  // Crear una arista con peso por defecto 1.0
  GraphEdge e = graph_edge(0, 1, 2.5f);
  ```

  ## Implementacion

  Los metodos `update_bounds()` y `find_node()` estan implementados en `cpp/functions/viz/graph_types.cpp`.

  `update_bounds()` es O(n) sobre `node_count`. Llamar despues de cada step del layout para mantener el bounding box fresco.

  `find_node()` es O(n) por busqueda lineal. Para grafos grandes (>10k nodos) considerar mantener un `unordered_map<uint32_t, int>` externo como indice.

code:
  #pragma once
  #include <cstdint>

  // --- Graph node ---
  struct GraphNode {
      uint32_t id;
      float x, y;            // position in layout space
      float vx, vy;          // velocity (used by force layout)
      float size;             // visual radius (default 4.0)
      uint32_t color;         // ABGR packed (0 = use default palette)
      const char* label;      // optional display label (nullptr = none)
      uint32_t community;     // group/cluster ID (for auto-coloring)
      float value;            // arbitrary metric (for sizing)
      bool pinned;            // if true, force layout won't move this node
  };

  // --- Graph edge ---
  struct GraphEdge {
      uint32_t source;        // index into GraphData::nodes
      uint32_t target;        // index into GraphData::nodes
      float weight;           // edge weight (affects attraction force)
      uint32_t color;         // ABGR packed (0 = default gray)
  };

  // --- Graph container ---
  struct GraphData {
      GraphNode* nodes;
      int node_count;
      GraphEdge* edges;
      int edge_count;

      // Bounding box (updated by layout)
      float min_x, min_y, max_x, max_y;

      // Recompute bounding box from node positions
      void update_bounds();

      // Find node index by id. Returns -1 if not found.
      int find_node(uint32_t id) const;
  };

  // --- Helper: create a default node ---
  inline GraphNode graph_node(uint32_t id, float x = 0, float y = 0) {
      return {id, x, y, 0, 0, 4.0f, 0, nullptr, 0, 0, false};
  }

  // --- Helper: create an edge ---
  inline GraphEdge graph_edge(uint32_t source, uint32_t target, float weight = 1.0f) {
      return {source, target, weight, 0};
  }


id: APIClientConfig_ts_core
name: APIClientConfig
lang: ts
domain: core
version: 1.0.0
algebraic: product
tags: ["api","client","config","http","fetch","auth"]
uses_types: []
file_path: frontend/types/core/api_client_config.ts
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: bacf1f04891a62b7055156bf98457f4c41384b7382dcb54d743b89e6b6b332a3
definition:
  interface APIClientConfig {
    base_url: string
    headers?: Record<string, string>
    on_error?: (error: Error, url: string, init?: RequestInit) => void
    on_unauthorized?: () => void
  }

description:
  Configuracion del cliente HTTP. Define base_url, headers fijos, callback de error global y callback de 401 Unauthorized.

notes:
  Pasado como primer argumento a `api_client_ts_core`. Los interceptors `on_error` y `on_unauthorized` son globales — aplican a todos los metodos (get, post, put, del, patch) del cliente creado.

documentation:
  ## Campos

  - `base_url` — prefijo de URL para todos los requests, sin slash final (ej: `"http://localhost:8484/api"`)
  - `headers` — headers HTTP fijos que se mergeaan en cada peticion (ej: `{ Authorization: 'Bearer ...' }`)
  - `on_error` — callback global llamado cuando cualquier peticion falla, recibe el error, la URL y el RequestInit
  - `on_unauthorized` — callback llamado cuando el servidor responde 401, tipicamente redirige al login

  ## Uso

  ```typescript
  import type { APIClientConfig } from '@/types/core/api_client_config'
  import { apiClient } from '@/functions/core/api_client'

  const config: APIClientConfig = {
    base_url: 'http://localhost:8484/api',
    headers: { 'X-Client': 'dashboard' },
    on_unauthorized: () => (window.location.href = '/login'),
  }
  const api = apiClient(config)
  ```

code:
  /** Configuracion del cliente HTTP creado por api_client. */
  export interface APIClientConfig {
    /** URL base para todas las peticiones (ej: "http://localhost:8484/api"). Sin slash final. */
    base_url: string
    /** Headers fijos que se incluyen en todas las peticiones */
    headers?: Record<string, string>
    /** Callback global de error — llamado en cualquier peticion fallida */
    on_error?: (error: Error, url: string, init?: RequestInit) => void
    /** Callback llamado cuando el servidor responde 401 Unauthorized */
    on_unauthorized?: () => void
  }


id: ComponentVariants_ts_core
name: ComponentVariants
lang: ts
domain: core
version: 1.0.0
algebraic: product
description: Tipos base para componentes con variantes CVA. Props comunes y composición de variantes type-safe.
tags: ["component","variants","cva","props","base"]
uses_types: []
file_path: frontend/types/core/component_variants.ts
created_at: 2026-04-03T01:01:56Z
updated_at: 2026-04-06T00:11:54Z
code:
  export interface ComponentBaseProps {
  className?: string
  children?: React.ReactNode
  }

content_hash: 22c4be3096963da8df3a3c251d0c31fdb688031935d5d300f2450c29eccb894a
source_repo: https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library
source_license: MIT
source_file: frontend/src/components/ui/button.tsx
definition:
  interface ComponentBaseProps { className?: string; children?: React.ReactNode }
  type PropsWithVariants<V> = ComponentBaseProps & VariantProps<V>

notes:
  Requiere `class-variance-authority` como dependencia del proyecto consumidor. `React.ReactNode` requiere que `react` esté en el scope — en proyectos con `@types/react` esto se resuelve automáticamente.

documentation:
  ## Tipos exportados

  - `VariantProps<V>` — re-exportado de `class-variance-authority`. Extrae el tipo de props de un `cva()` call.
  - `ComponentBaseProps` — props comunes a todos los componentes: `className` y `children` opcionales.
  - `PropsWithVariants<V>` — combinación de `ComponentBaseProps` con las variantes de un `cva()` concreto. Usar como base para las props de cualquier componente con variantes.

  ## Uso

  ```typescript
  import { cva } from "class-variance-authority"
  import { type PropsWithVariants } from "@/types/core/component_variants"

  const buttonVariants = cva("base-classes", {
    variants: { size: { sm: "...", md: "..." } }
  })

  type ButtonProps = PropsWithVariants<typeof buttonVariants>
  ```


id: FetchState_ts_core
name: FetchState
lang: ts
domain: core
version: 1.0.0
algebraic: product
tags: ["fetch","state","http","cache","swr","hook"]
uses_types: []
file_path: frontend/types/core/fetch_state.ts
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: c74410a98d66c3c08552dce86f1a600d473c8cd01f8f2ddf7e678b358a9918a1
definition:
  interface FetchState<T> {
    data: T | null
    error: Error | null
    loading: boolean
    refetch: () => Promise<T>
    is_stale: boolean
  }

description:
  Estado de un fetch GET con cache stale-while-revalidate. Incluye datos, error, loading, refetch y flag de staleness.

notes:
  Retornado por `use_fetch_ts_core`. El patron stale-while-revalidate permite mostrar datos viejos inmediatamente mientras se actualizan en background, mejorando la UX percibida.

documentation:
  ## Campos

  - `data` — datos obtenidos, null hasta que llega la primera respuesta
  - `error` — error de red o de parsing, null si no hubo error
  - `loading` — true mientras hay una peticion en vuelo
  - `refetch` — funcion para re-ejecutar la peticion manualmente
  - `is_stale` — true cuando se muestran datos del cache mientras se refetchea en background

  ## Uso

  ```typescript
  import type { FetchState } from '@/types/core/fetch_state'

  function useMyHook(): FetchState<User[]> {
    // ...
  }
  ```

code:
  /** Estado de un fetch GET con cache stale-while-revalidate. */
  export interface FetchState<T> {
    /** Datos obtenidos, null si todavia no se han cargado */
    data: T | null
    /** Error si la peticion fallo */
    error: Error | null
    /** Si la peticion esta en progreso */
    loading: boolean
    /** Re-ejecutar la peticion manualmente */
    refetch: () => Promise<T>
    /** Si los datos son stale (cache viejo mostrando mientras refetch en background) */
    is_stale: boolean
  }


id: FormState_ts_core
name: FormState
lang: ts
domain: core
version: 1.0.0
algebraic: product
description: Estado de un formulario con validacion sincrona, tracking de touched y flag de submit en progreso.
tags: ["form","state","validation","touched","hook"]
uses_types: []
file_path: frontend/types/core/form_state.ts
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 5d844c4147499a4bb93de8470b237dfcfc1270d521c983f9c76aa0506c727a4f
definition:
  interface FormState<T extends Record<string, unknown>> {
    values: T
    errors: Partial<Record<keyof T, string>>
    touched: Partial<Record<keyof T, boolean>>
    is_valid: boolean
    is_submitting: boolean
  }

notes:
  Retornado por `use_form_ts_core`. El generico `T extends Record<string, unknown>` permite tipado completo de los campos y sus errores. Para formularios complejos con validacion async o arrays de campos, usar `@mantine/form` directamente.

documentation:
  ## Campos

  - `values` — valores actuales del formulario, tipados con el generico T
  - `errors` — errores de validacion por campo (string con mensaje), undefined si el campo es valido
  - `touched` — true por campo cuando el usuario lo ha modificado (util para mostrar errores solo al tocar)
  - `is_valid` — true cuando no hay ningun error de validacion en ningun campo
  - `is_submitting` — true mientras el handler de submit esta en ejecucion

  ## Uso

  ```typescript
  import type { FormState } from '@/types/core/form_state'

  interface LoginForm { email: string; password: string }

  function LoginPage() {
    const form: FormState<LoginForm> = useForm({ initial_values: { email: '', password: '' } })
  }
  ```

code:
  /** Estado de un formulario con validacion sincrona. */
  export interface FormState<T extends Record<string, unknown>> {
    /** Valores actuales del formulario */
    values: T
    /** Errores por campo (undefined si el campo no tiene error) */
    errors: Partial<Record<keyof T, string>>
    /** Campos que el usuario ha modificado al menos una vez */
    touched: Partial<Record<keyof T, boolean>>
    /** Si todos los campos pasan la validacion */
    is_valid: boolean
    /** Si el submit esta en progreso */
    is_submitting: boolean
  }


id: MutationState_ts_core
name: MutationState
lang: ts
domain: core
version: 1.0.0
algebraic: product
description: Estado de una mutacion POST/PUT/DELETE. Expone mutate, mutate_async, data, error, loading y reset.
tags: ["mutation","state","http","post","put","delete","hook"]
uses_types: []
file_path: frontend/types/core/mutation_state.ts
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 757f1e3777d4dbc5d9d256de00c124d8d7aca190c8c65281a94454e1f25bafa0
definition:
  interface MutationState<TData, TVariables> {
    mutate: (variables: TVariables) => void
    mutate_async: (variables: TVariables) => Promise<TData>
    data: TData | null
    error: Error | null
    loading: boolean
    reset: () => void
  }

notes:
  Retornado por `use_mutation_ts_core`. El patron `mutate` / `mutate_async` permite usar el hook tanto en event handlers (sin async/await) como en funciones que necesitan esperar el resultado.

documentation:
  ## Campos

  - `mutate` — ejecutar la mutacion de forma fire-and-forget (no lanza excepciones)
  - `mutate_async` — ejecutar la mutacion y obtener una Promise (puede rechazar)
  - `data` — resultado de la ultima mutacion exitosa, null si aun no hubo exito
  - `error` — error de la ultima mutacion fallida, null si fue exitosa o no se ha ejecutado
  - `loading` — true mientras la mutacion esta en vuelo
  - `reset` — volver al estado inicial (idle, data null, error null)

  ## Uso

  ```typescript
  import type { MutationState } from '@/types/core/mutation_state'

  function useCreateUser(): MutationState<User, CreateUserInput> {
    // ...
  }
  ```

code:
  /** Estado de una mutacion POST/PUT/DELETE. */
  export interface MutationState<TData, TVariables> {
    /** Ejecutar la mutacion (fire-and-forget) */
    mutate: (variables: TVariables) => void
    /** Ejecutar la mutacion y esperar el resultado */
    mutate_async: (variables: TVariables) => Promise<TData>
    /** Datos del resultado de la ultima mutacion exitosa */
    data: TData | null
    /** Error si la ultima mutacion fallo */
    error: Error | null
    /** Si hay una mutacion en progreso */
    loading: boolean
    /** Resetear al estado inicial (idle) */
    reset: () => void
  }


id: ChartSeries_ts_ui
name: ChartSeries
lang: ts
domain: ui
version: 1.0.0
algebraic: product
description: Tipos base para series y datos de gráficos. Usados por todos los chart components.
tags: ["chart","series","data","visualization"]
uses_types: []
file_path: frontend/types/ui/chart_series.ts
created_at: 2026-04-03T01:01:56Z
updated_at: 2026-04-03T01:01:56Z
content_hash: 98765369ddb095007d1623a20d61147c3ae4187ad5c57f73035aa804e4a627fc
source_repo: https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library
source_license: MIT
source_file: frontend/src/components/ui/charts/chart-base.tsx
definition:
  interface Series { key: string; name: string; color?: string }
  interface ChartDataPoint { [key: string]: string | number }

notes:
  Tipos mínimos sin dependencias. `ChartDataPoint` usa index signature para permitir cualquier combinación de campos numéricos y categóricos en el mismo punto de datos.

documentation:
  ## Interfaces exportadas

  - `Series` — describe una serie de datos: `key` (campo en el datapoint), `name` (label visible), `color` opcional (override del color del tema)
  - `ChartDataPoint` — mapa genérico `string -> string | number`, representa un punto de datos indexado por clave

code:
  export interface Series {
    key: string
    name: string
    color?: string
  }

  export interface ChartDataPoint {
    [key: string]: string | number
  }


id: ThemeConfig_ts_ui
name: ThemeConfig
lang: ts
domain: ui
version: 1.0.0
algebraic: product
tags: ["theme","design-tokens","colors","typography","ui"]
uses_types: []
file_path: frontend/types/ui/theme_config.ts
created_at: 2026-04-03T01:01:56Z
updated_at: 2026-04-03T01:01:56Z
content_hash: 2d869d04b5b00fc3ef97737a544b8ba9fa6934a8c39337665038a2e636d3e2f4
source_repo: https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library
source_license: MIT
source_file: frontend/src/themes/types.ts
definition:
  interface ThemeConfig { name: string; label: string; palette: ColorPalette; colors: SemanticColors; typography: Typography; spacing: Spacing; borders: Borders; shadows: Shadows; motion: Motion; breakpoints: Breakpoints; zIndex: ZIndex; icons: Icons }

description:
  Sistema completo de tokens de diseño: colores semánticos, tipografía, spacing, sombras, motion, breakpoints. Base del theming de Frontend Library.

notes:
  Copia directa de `frontend/src/themes/types.ts` de Frontend_Library. El archivo es autocontenido sin dependencias externas. Los colores usan formato OKLCH via variables CSS.

documentation:
  ## Interfaces exportadas

  - `ColorPalette` — colores primitivos (grays, brand, status, white/black)
  - `SemanticColors` — tokens semánticos (background, foreground, border, brand, status, surface, sidebar, chart)
  - `Typography` — fontFamily, fontSize, fontWeight, lineHeight, letterSpacing
  - `Spacing` — escala 0-96 basada en 4px
  - `Borders` — width y radius
  - `Shadows` — elevación xs-2xl, inner, glow
  - `Motion` — duration e easing
  - `Breakpoints` — xs-2xl
  - `ZIndex` — jerarquía de capas hide-max
  - `Icons` — size y strokeWidth para Phosphor Icons
  - `ThemeConfig` — composición completa de todos los tokens
  - `ThemeColors` — formato legacy para compatibilidad con shadcn/ui
  - `Theme` — nombre + label + ThemeColors
  - `ThemeName` — union type de los temas disponibles

  ## Helper

  `themeConfigToColors(config: ThemeConfig): ThemeColors` — convierte un ThemeConfig al formato legacy ThemeColors que consume shadcn/ui.

code:
  // ============================================================================
  // THEME SYSTEM TYPES
  // Sistema de temas completo para Frontend Library
  // ============================================================================

  // ----------------------------------------------------------------------------
  // 1. COLOR PALETTE (Primitivos)
  // ----------------------------------------------------------------------------

  export interface ColorPalette {
    // Grays (escala neutral)
    gray50: string
    gray100: string
    gray200: string
    gray300: string
    gray400: string
    gray500: string
    gray600: string
    gray700: string
    gray800: string
    gray900: string
    gray950: string

    // Brand colors (personalizables por tema)
    brand50: string
    brand100: string
    brand200: string
    brand300: string
    brand400: string
    brand500: string
    brand600: string
    brand700: string
    brand800: string
    brand900: string
    brand950: string

    // Status colors
    red500: string
    red600: string
    green500: string
    green600: string
    yellow500: string
    yellow600: string
    blue500: string
    blue600: string

    // Pure colors
    white: string
    black: string
    transparent: string
  }

  // ----------------------------------------------------------------------------
  // 2. SEMANTIC COLORS (Tokens semánticos)
  // ----------------------------------------------------------------------------

  export interface SemanticColors {
    // Backgrounds
    background: {
      default: string
      subtle: string
      muted: string
      inverse: string
    }

    // Foregrounds
    foreground: {
      default: string
      muted: string
      subtle: string
      disabled: string
      inverse: string
    }

    // Borders
    border: {
      default: string
      muted: string
      strong: string
      focus: string
    }

    // Brand
    brand: {
      primary: string
      primaryForeground: string
      secondary: string
      secondaryForeground: string
      accent: string
      accentForeground: string
    }

    // Status
    status: {
      success: string
      successForeground: string
      successMuted: string
      warning: string
      warningForeground: string
      warningMuted: string
      error: string
      errorForeground: string
      errorMuted: string
      info: string
      infoForeground: string
      infoMuted: string
    }

    // Surfaces (jerarquía de capas)
    surface: {
      base: string        // Fondo principal
      raised: string      // Cards, panels
      overlay: string     // Modals, dropdowns
      sunken: string      // Inputs, wells
    }

    // Sidebar (panel lateral oscuro)
    sidebar: {
      background: string
      foreground: string
      muted: string
      accent: string
      accentForeground: string
      border: string
      ring: string
    }

    // Charts
    chart: {
      1: string
      2: string
      3: string
      4: string
      5: string
      6: string
      7: string
      8: string
    }

    // Interactive
    ring: string
    input: string
  }

  // ----------------------------------------------------------------------------
  // 3. TYPOGRAPHY
  // ----------------------------------------------------------------------------

  export interface Typography {
    fontFamily: {
      display: string     // Títulos destacados
      heading: string     // Encabezados
      body: string        // Texto general
      mono: string        // Código
    }

    fontSize: {
      xs: string          // 12px
      sm: string          // 14px
      base: string        // 16px
      lg: string          // 18px
      xl: string          // 20px
      '2xl': string       // 24px
      '3xl': string       // 30px
      '4xl': string       // 36px
      '5xl': string       // 48px
      '6xl': string       // 60px
    }

    fontWeight: {
      light: number
      normal: number
      medium: number
      semibold: number
      bold: number
      extrabold: number
    }

    lineHeight: {
      none: number
      tight: number
      snug: number
      normal: number
      relaxed: number
      loose: number
    }

    letterSpacing: {
      tighter: string
      tight: string
      normal: string
      wide: string
      wider: string
    }
  }

  // ----------------------------------------------------------------------------
  // 4. SPACING
  // ----------------------------------------------------------------------------

  export interface Spacing {
    0: string
    px: string
    0.5: string
    1: string
    1.5: string
    2: string
    2.5: string
    3: string
    3.5: string
    4: string
    5: string
    6: string
    7: string
    8: string
    9: string
    10: string
    11: string
    12: string
    14: string
    16: string
    20: string
    24: string
    28: string
    32: string
    36: string
    40: string
    44: string
    48: string
    52: string
    56: string
    60: string
    64: string
    72: string
    80: string
    96: string
  }

  // ----------------------------------------------------------------------------
  // 5. BORDERS
  // ----------------------------------------------------------------------------

  export interface Borders {
    width: {
      none: string
      thin: string
      default: string
      medium: string
      thick: string
    }

    radius: {
      none: string
      sm: string
      default: string
      md: string
      lg: string
      xl: string
      '2xl': string
      '3xl': string
      full: string
    }
  }

  // ----------------------------------------------------------------------------
  // 6. SHADOWS (Elevation)
  // ----------------------------------------------------------------------------

  export interface Shadows {
    none: string
    xs: string
    sm: string
    default: string
    md: string
    lg: string
    xl: string
    '2xl': string
    inner: string
    // Colored shadows for glow effects
    glow: {
      sm: string
      md: string
      lg: string
    }
  }

  // ----------------------------------------------------------------------------
  // 7. MOTION / ANIMATION
  // ----------------------------------------------------------------------------

  export interface Motion {
    duration: {
      instant: string     // 0ms
      fast: string        // 100ms
      normal: string      // 200ms
      slow: string        // 300ms
      slower: string      // 500ms
      slowest: string     // 1000ms
    }

    easing: {
      linear: string
      default: string
      in: string
      out: string
      inOut: string
      spring: string
      bounce: string
    }
  }

  // ----------------------------------------------------------------------------
  // 8. BREAKPOINTS
  // ----------------------------------------------------------------------------

  export interface Breakpoints {
    xs: string
    sm: string
    md: string
    lg: string
    xl: string
    '2xl': string
  }

  // ----------------------------------------------------------------------------
  // 9. Z-INDEX
  // ----------------------------------------------------------------------------

  export interface ZIndex {
    hide: number
    base: number
    raised: number
    dropdown: number
    sticky: number
    overlay: number
    modal: number
    popover: number
    toast: number
    tooltip: number
    max: number
  }

  // ----------------------------------------------------------------------------
  // 10. ICONS
  // ----------------------------------------------------------------------------

  export interface Icons {
    size: {
      xs: string          // 12px
      sm: string          // 16px
      default: string     // 20px
      md: string          // 24px
      lg: string          // 32px
      xl: string          // 40px
      '2xl': string       // 48px
    }

    strokeWidth: {
      thin: number
      light: number
      regular: number
      bold: number
      fill: number
    }
  }

  // ----------------------------------------------------------------------------
  // THEME CONFIGURATION (Complete)
  // ----------------------------------------------------------------------------

  export interface ThemeConfig {
    // Metadata
    name: string
    label: string
    description?: string

    // Color system
    palette: ColorPalette
    colors: SemanticColors

    // Typography
    typography: Typography

    // Layout
    spacing: Spacing
    borders: Borders
    shadows: Shadows

    // Interaction
    motion: Motion

    // Responsive
    breakpoints: Breakpoints
    zIndex: ZIndex

    // Icons
    icons: Icons
  }

  // ----------------------------------------------------------------------------
  // LEGACY SUPPORT - ThemeColors (for backward compatibility)
  // ----------------------------------------------------------------------------

  export interface ThemeColors {
    // Base
    background: string
    foreground: string

    // Cards & Popovers
    card: string
    cardForeground: string
    popover: string
    popoverForeground: string

    // Primary & Secondary
    primary: string
    primaryForeground: string
    secondary: string
    secondaryForeground: string

    // Muted & Accent
    muted: string
    mutedForeground: string
    accent: string
    accentForeground: string

    // Semantic colors
    destructive: string
    destructiveForeground: string
    success: string
    successForeground: string
    warning: string
    warningForeground: string
    info: string
    infoForeground: string

    // Surfaces
    surface: string
    surfaceHover: string
    overlay: string

    // Form elements
    border: string
    input: string
    ring: string

    // Charts
    chart1: string
    chart2: string
    chart3: string
    chart4: string
    chart5: string

    // Sidebar
    sidebar: string
    sidebarForeground: string
    sidebarPrimary: string
    sidebarPrimaryForeground: string
    sidebarAccent: string
    sidebarAccentForeground: string
    sidebarBorder: string
    sidebarRing: string
  }

  export interface Theme {
    name: string
    label: string
    colors: ThemeColors
  }

  export type ThemeName = 'default' | 'dark' | 'blue' | 'green' | 'purple' | 'orange' | 'rose' | 'cyan' | 'sunset' | 'midnight'

  // ----------------------------------------------------------------------------
  // HELPER: Convert ThemeConfig to legacy ThemeColors
  // ----------------------------------------------------------------------------

  export function themeConfigToColors(config: ThemeConfig): ThemeColors {
    const { colors } = config

    return {
      // Base
      background: colors.background.default,
      foreground: colors.foreground.default,

      // Cards & Popovers
      card: colors.surface.raised,
      cardForeground: colors.foreground.default,
      popover: colors.surface.overlay,
      popoverForeground: colors.foreground.default,

      // Primary & Secondary
      primary: colors.brand.primary,
      primaryForeground: colors.brand.primaryForeground,
      secondary: colors.brand.secondary,
      secondaryForeground: colors.brand.secondaryForeground,

      // Muted & Accent
      muted: colors.background.muted,
      mutedForeground: colors.foreground.muted,
      accent: colors.brand.accent,
      accentForeground: colors.brand.accentForeground,

      // Semantic colors
      destructive: colors.status.error,
      destructiveForeground: colors.status.errorForeground,
      success: colors.status.success,
      successForeground: colors.status.successForeground,
      warning: colors.status.warning,
      warningForeground: colors.status.warningForeground,
      info: colors.status.info,
      infoForeground: colors.status.infoForeground,

      // Surfaces
      surface: colors.surface.raised,
      surfaceHover: colors.background.subtle,
      overlay: colors.surface.overlay,

      // Form elements
      border: colors.border.default,
      input: colors.border.default,
      ring: colors.ring,

      // Charts
      chart1: colors.chart[1],
      chart2: colors.chart[2],
      chart3: colors.chart[3],
      chart4: colors.chart[4],
      chart5: colors.chart[5],

      // Sidebar
      sidebar: colors.sidebar.background,
      sidebarForeground: colors.sidebar.foreground,
      sidebarPrimary: colors.brand.primary,
      sidebarPrimaryForeground: colors.brand.primaryForeground,
      sidebarAccent: colors.sidebar.accent,
      sidebarAccentForeground: colors.sidebar.accentForeground,
      sidebarBorder: colors.sidebar.border,
      sidebarRing: colors.sidebar.ring,
    }
  }


id: WailsIPC_ts_ui
name: WailsIPC
lang: ts
domain: ui
version: 1.0.0
algebraic: product
tags: ["wails","ipc","types","query","mutation"]
uses_types: []
file_path: frontend/types/ui/wails_ipc.ts
created_at: 2026-04-03T01:01:56Z
updated_at: 2026-04-03T01:01:56Z
content_hash: ebcba7837d5cdefff5ec2d5afdc39b9a8ec09004c049606aba1e35707e6a0e59
source_repo: https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library
source_license: MIT
source_file: frontend/src/lib/wails/types.ts
definition:
  interface QueryState<T> { data: T | null; loading: boolean; error: Error | null; isStale: boolean; lastUpdated: Date | null }
  interface QueryOptions { enabled?: boolean; refetchInterval?: number; refetchOnFocus?: boolean; staleTime?: number; cacheTime?: number; retry?: number | boolean; retryDelay?: number }
  interface MutationOptions { retry?: number | boolean; retryDelay?: number }

description:
  Tipos base para el sistema IPC de Wails: QueryState, QueryOptions, MutationOptions, WailsEvent, defaults.

notes:
  Incluye constantes DEFAULT_QUERY_OPTIONS y DEFAULT_MUTATION_OPTIONS con valores sensibles para IPC (staleTime: 0, cacheTime: 5min, retry: 3).

code:
  /** Estado de una query */
  export interface QueryState<T> {
    data: T | null
    loading: boolean
    error: Error | null
    isStale: boolean
    lastUpdated: Date | null
  }

  /** Opciones comunes para queries */
  export interface QueryOptions {
    /** Ejecutar automáticamente (default: true) */
    enabled?: boolean
    /** Auto-refetch cada N ms */
    refetchInterval?: number
    /** Refetch cuando la ventana gana foco */
    refetchOnFocus?: boolean
    /** Tiempo en ms antes de considerar datos stale (default: 0) */
    staleTime?: number
    /** Tiempo en ms en cache después de unmount (default: 5 min) */
    cacheTime?: number
    /** Número de reintentos en error (default: 3) */
    retry?: number | boolean
    /** Delay entre reintentos en ms */
    retryDelay?: number
  }

  /** Opciones para mutations */
  export interface MutationOptions {
    /** Reintentos en error */
    retry?: number | boolean
    /** Delay entre reintentos */
    retryDelay?: number
  }

  /** Resultado de un evento Wails */
  export interface WailsEvent<T> {
    name: string
    data: T
    timestamp: Date
  }

  /** Estado de una mutation */
  export interface MutationState<T> {
    data: T | null
    loading: boolean
    error: Error | null
  }

  /** Default options */
  export const DEFAULT_QUERY_OPTIONS: Required<QueryOptions> = {
    enabled: true,
    refetchInterval: 0,
    refetchOnFocus: true,
    staleTime: 0,
    cacheTime: 5 * 60 * 1000, // 5 minutos
    retry: 3,
    retryDelay: 1000,
  }

  export const DEFAULT_MUTATION_OPTIONS: Required<MutationOptions> = {
    retry: 0,
    retryDelay: 1000,
  }


id: agent_action_py_core
name: agent_action
lang: py
domain: core
version: 1.0.0
algebraic: product
description: Registro de una accion individual de un agente en una simulacion multi-agente.
tags: ["simulation","agent","action","multi-agent","event"]
uses_types: []
file_path: python/types/core/agent_action.py
created_at: 2026-04-05T11:19:05Z
updated_at: 2026-04-05T11:19:05Z
content_hash: 87a517c6f7fc953e5e27d6beb26f39360493b34f62ccc3acabaeaa1a927323b7
definition:
  @dataclass
  class AgentAction:
      round_num: int
      timestamp: str
      platform: str
      agent_id: int
      agent_name: str
      action_type: str
      action_args: dict = field(default_factory=dict)
      result: str | None = None
      success: bool = True

examples:
  ```python
  action = AgentAction(
      round_num=1,
      timestamp="2024-01-01T12:00:00Z",
      platform="twitter",
      agent_id=42,
      agent_name="bot_alpha",
      action_type="CREATE_POST",
      action_args={"content": "Hello world"},
      success=True,
  )
  print(action.to_dict())
  ```

notes:
  Tipo producto — todos los campos obligatorios estan siempre presentes. `action_args` es un dict abierto para que el caller especifique los parametros sin restricciones de schema. `result` es opcional porque puede no haber sido resuelto aun en el momento de creacion.

code:
  """Registro de una accion individual de un agente en una simulacion multi-agente."""

  from __future__ import annotations

  from dataclasses import dataclass, field


  @dataclass
  class AgentAction:
      """Registro de una accion individual de un agente en una simulacion multi-agente.

      Captura que hizo el agente, cuando, en que plataforma, y si fue exitoso.

      Attributes:
          round_num: Ronda de la simulacion en la que ocurrio la accion.
          timestamp: Timestamp ISO 8601 del momento de la accion.
          platform: Plataforma donde ocurrio ("twitter", "reddit", o custom).
          agent_id: ID numerico del agente.
          agent_name: Nombre del agente.
          action_type: Tipo de accion ("CREATE_POST", "LIKE_POST", "COMMENT", etc.).
          action_args: Parametros especificos de la accion.
          result: Resultado de la accion (None si aun no hay resultado).
          success: Si la accion fue exitosa.
      """

      round_num: int
      timestamp: str
      platform: str
      agent_id: int
      agent_name: str
      action_type: str
      action_args: dict = field(default_factory=dict)
      result: str | None = None
      success: bool = True

      def to_dict(self) -> dict:
          """Convierte la accion a un diccionario serializable."""
          return {
              "round_num": self.round_num,
              "timestamp": self.timestamp,
              "platform": self.platform,
              "agent_id": self.agent_id,
              "agent_name": self.agent_name,
              "action_type": self.action_type,
              "action_args": self.action_args,
              "result": self.result,
              "success": self.success,
          }


id: base_parser_py_core
name: base_parser
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["parser","protocol","abstract","extensible","plugin"]
uses_types: ["parse_result_py_core"]
file_path: python/types/core/base_parser.py
created_at: 2026-04-05T11:24:00Z
updated_at: 2026-04-05T11:24:00Z
content_hash: 7825732c89fc9960edcc5372674aed6de36e8b2e92ca4d01edc78e9733c7ef40
definition:
  class BaseParser(ABC):
      @property
      @abstractmethod
      def supported_extensions(self) -> list[str]: ...

      @abstractmethod
      async def parse(self, source: str | Path, **kwargs) -> ParseResult: ...

      @abstractmethod
      async def parse_content(self, content: str, source_path: str | None = None, **kwargs) -> ParseResult: ...

      def can_parse(self, path: str) -> bool:
          return Path(path).suffix.lower() in self.supported_extensions

description:
  Protocolo abstracto que todo parser del registry debe implementar. Define la interfaz minima para parsers extensibles: extensiones soportadas, parse desde path y parse desde contenido en memoria. El ParserRegistry usa esta interfaz para despachar al parser correcto segun extension de archivo.

notes:
  Clase base abstracta (ABC) con tres metodos obligatorios y un helper concreto `can_parse`. Los parsers concretos heredan de BaseParser e implementan los tres metodos abstractos.

  El metodo `can_parse` es concreto — verifica la extension del archivo contra `supported_extensions`. No es necesario sobreescribirlo salvo casos especiales.

  Disenado para composicion con ParserRegistry: el registry registra instancias de BaseParser y las resuelve automaticamente por extension de archivo.

code:
  """BaseParser — protocolo abstracto para parsers del registry."""

  from __future__ import annotations

  from abc import ABC, abstractmethod
  from pathlib import Path
  from typing import TYPE_CHECKING

  if TYPE_CHECKING:
      from parse_result import ParseResult


  class BaseParser(ABC):
      """Protocolo abstracto que todo parser del registry debe implementar.

      Define la interfaz minima para parsers extensibles: soportar extensiones
      de archivo, parsear desde path o desde contenido en memoria. El
      ParserRegistry usa esta interfaz para despachar parsing al parser correcto.

      Attributes:
          supported_extensions: Lista de extensiones que este parser maneja
                                (ej: [".md", ".markdown"]).
      """

      @property
      @abstractmethod
      def supported_extensions(self) -> list[str]:
          """Lista de extensiones soportadas (con punto, lowercase).

          Returns:
              Lista de strings como [".md", ".markdown"].
          """
          ...

      @abstractmethod
      async def parse(self, source: str | Path, **kwargs) -> "ParseResult":
          """Parsea un archivo desde su path o URL.

          Args:
              source: Path al archivo, directorio o URL a parsear.
              **kwargs: Argumentos adicionales especificos del parser.

          Returns:
              ParseResult con el arbol de nodos del documento.
          """
          ...

      @abstractmethod
      async def parse_content(
          self,
          content: str,
          source_path: str | None = None,
          **kwargs,
      ) -> "ParseResult":
          """Parsea un string de contenido directamente en memoria.

          Args:
              content:     Contenido del documento como string.
              source_path: Path original opcional, solo para metadata.
              **kwargs:    Argumentos adicionales especificos del parser.

          Returns:
              ParseResult con el arbol de nodos del documento.
          """
          ...

      def can_parse(self, path: str) -> bool:
          """Indica si este parser puede manejar el archivo dado.

          Args:
              path: Path del archivo a verificar.

          Returns:
              True si la extension del archivo esta en supported_extensions.
          """
          return Path(path).suffix.lower() in self.supported_extensions


id: code_entity_py_core
name: code_entity
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["ast","code","parsing","tree-sitter","symbol","entity","core"]
uses_types: []
file_path: python/types/core/code_entity.py
created_at: 2026-04-05T11:20:30Z
updated_at: 2026-04-05T11:20:30Z
content_hash: da929efef077a638aafc203aed0695f26736456e4eb18664e0c126677d05b621
definition:
  @dataclass
  class CodeEntity:
      kind: str                    # "function" | "class" | "method" | "interface" | "type" | "struct" | "trait"
      name: str                    # nombre de la entidad
      start_line: int              # linea de inicio (1-based)
      end_line: int                # linea de fin (1-based)
      signature: str               # firma completa hasta el body
      docstring: str | None        # docstring/comment extraido
      language: str                # "python" | "go" | "typescript" | "rust" | "java" | "cpp"
      children: list[CodeEntity] = field(default_factory=list)

description:
  Entidad de codigo extraida por analisis AST (tree-sitter). Representa una funcion, clase, metodo, tipo, interfaz, struct o trait encontrado en codigo fuente. Las clases contienen sus metodos como children.

examples:
  ```python
  from code_entity import CodeEntity

  # Funcion standalone
  fn = CodeEntity(
      kind="function",
      name="parse_file",
      start_line=10,
      end_line=35,
      signature="def parse_file(path: str) -> list[CodeEntity]:",
      docstring="Parsea un archivo de codigo y retorna sus entidades.",
      language="python",
  )

  print(fn.line_count)     # 26
  print(fn.has_docstring)  # True

  # Clase con metodos como children
  cls = CodeEntity(
      kind="class",
      name="Parser",
      start_line=1,
      end_line=80,
      signature="class Parser:",
      docstring=None,
      language="python",
      children=[fn],
  )

  data = cls.to_dict()
  restored = CodeEntity.from_dict(data)
  assert restored.name == "Parser"
  assert restored.children[0].name == "parse_file"
  ```

notes:
  - `signature` incluye decoradores/annotations en Python, generic params en TypeScript/Go, visibility modifiers en Java/Rust. Es todo el texto desde el inicio de la definicion hasta el inicio del body (excluyendo `:`).
  - `docstring` es el primer string literal hijo en Python, el comentario `///` o `/** */` previo en otros lenguajes. Puede ser None si la entidad no esta documentada.
  - `children` solo se usa para metodos dentro de clases/structs/traits/interfaces. Las funciones standalone tienen `children = []`.
  - El arbol de children es un nivel de profundidad en la mayoria de los casos (clase -> metodos). Traits con associated types pueden tener mas niveles.
  - Tipo pensado como resultado de `parse_code_ast()`. Util para indexar codigo, generar skeletons, extraer candidatos de repos externos y navegacion (jump to definition).

documentation:
  ## Propiedades

  - `line_count -> int` (property) — numero de lineas de la entidad: `end_line - start_line + 1`
  - `has_docstring -> bool` (property) — True si `docstring` es no nulo y no vacio

  ## Metodos

  - `to_dict() -> dict` — serializa la entidad de forma recursiva, incluyendo `children`
  - `from_dict(data: dict) -> CodeEntity` (classmethod) — deserializa desde un dict, reconstruyendo el arbol de children

  ## Valores esperados

  | Campo | Valores |
  |---|---|
  | `kind` | `"function"`, `"class"`, `"method"`, `"interface"`, `"type"`, `"struct"`, `"trait"` |
  | `language` | `"python"`, `"go"`, `"typescript"`, `"rust"`, `"java"`, `"cpp"` |

code:
  """CodeEntity — entidad de codigo extraida por analisis AST."""

  from __future__ import annotations

  from dataclasses import dataclass, field


  @dataclass
  class CodeEntity:
      """Entidad de codigo extraida por analisis AST (tree-sitter).

      Representa una funcion, clase, metodo, tipo, interfaz, struct o trait
      encontrado en codigo fuente. Las clases contienen sus metodos como children.
      """

      kind: str
      name: str
      start_line: int
      end_line: int
      signature: str
      docstring: str | None
      language: str
      children: list[CodeEntity] = field(default_factory=list)

      @property
      def line_count(self) -> int:
          """Numero de lineas que ocupa la entidad."""
          return self.end_line - self.start_line + 1

      @property
      def has_docstring(self) -> bool:
          """True si la entidad tiene docstring o comentario documentado."""
          return self.docstring is not None and len(self.docstring.strip()) > 0

      def to_dict(self) -> dict:
          """Serializa la entidad a un diccionario de forma recursiva."""
          return {
              "kind": self.kind,
              "name": self.name,
              "start_line": self.start_line,
              "end_line": self.end_line,
              "signature": self.signature,
              "docstring": self.docstring,
              "language": self.language,
              "children": [child.to_dict() for child in self.children],
          }

      @classmethod
      def from_dict(cls, data: dict) -> CodeEntity:
          """Deserializa una entidad desde un diccionario (recursivo)."""
          children = [cls.from_dict(c) for c in data.get("children", [])]
          return cls(
              kind=data["kind"],
              name=data["name"],
              start_line=data["start_line"],
              end_line=data["end_line"],
              signature=data["signature"],
              docstring=data.get("docstring"),
              language=data["language"],
              children=children,
          )


id: context_py_core
name: context
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["context","rag","vector-db","retrieval","memory","resource","hierarchy","hotness"]
uses_types: ["context_level_py_core","resource_content_type_py_core"]
file_path: python/types/core/context.py
created_at: 2026-04-05T11:21:07Z
updated_at: 2026-04-05T11:21:07Z
content_hash: e499bb055a744fdcaef5aaef8ab54533719e8260b9a5765da1badf097198d537
definition:
  @dataclass
  class Context:
      id: str
      uri: str
      parent_uri: str | None = None
      is_leaf: bool = False
      abstract: str = ""
      context_type: str = "resource"
      category: str = ""
      level: int | None = None
      created_at: datetime | None = None
      updated_at: datetime | None = None
      active_count: int = 0
      related_uri: list[str] = field(default_factory=list)
      meta: dict[str, Any] = field(default_factory=dict)

description:
  Modelo unificado de contexto que representa cualquier recurso (documento, memoria, skill) indexable y buscable. Combina metadata, jerarquia via URI, conteo de accesos para hotness scoring, y relaciones entre contextos.

notes:
  El campo `uri` sigue un esquema tipo filesystem: `viking://resources/docs/readme.md`. El campo `parent_uri` permite reconstruir la jerarquia de directorios sin necesidad de un grafo explicito.

  El campo `active_count` se incrementa cada vez que el contexto se recupera — usado por `hotness_score()` para priorizar resultados frecuentes sobre resultados menos accedidos.

  El campo `level` referencia los valores de `ContextLevel` (0=ABSTRACT, 1=OVERVIEW, 2=DETAIL). Se almacena como `int | None` para evitar acoplar el tipo a la importacion del enum.

  El campo `related_uri` permite modelar grafos de relaciones entre contextos (links, referencias, dependencias) sin necesidad de una tabla de relaciones separada.

  Los metodos `to_dict()` y `from_dict()` son inversos entre si: `Context.from_dict(ctx.to_dict()) == ctx` (asumiendo que los timestamps son objetos datetime, no strings).

code:
  """Context — modelo unificado de contexto indexable y buscable."""

  from __future__ import annotations

  from dataclasses import dataclass, field
  from datetime import datetime
  from typing import Any


  @dataclass
  class Context:
      """Modelo unificado de contexto que representa cualquier recurso indexable.

      Representa documentos, memorias o skills almacenados en una base de datos
      vectorial. Combina metadata, jerarquia de directorios (via URI), conteo de
      accesos para hotness scoring, y relaciones entre contextos.

      Attributes:
          id:           UUID unico del contexto.
          uri:          Identificador unico del recurso, estilo filesystem.
                        Ejemplo: ``viking://resources/docs/readme.md``
          parent_uri:   URI del nodo padre (directorio que lo contiene).
                        None si es la raiz.
          is_leaf:      True si es un nodo hoja (archivo), False si es directorio.
          abstract:     Resumen corto del contenido (L0, ~256 chars).
          context_type: Categoria semantica del contexto.
                        Valores tipicos: "memory", "resource", "skill".
          category:     Subcategoria libre para clasificacion adicional.
          level:        Nivel de detalle (ContextLevel: 0=ABSTRACT, 1=OVERVIEW, 2=DETAIL).
                        None si no se ha asignado nivel.
          created_at:   Timestamp de creacion (UTC). None si desconocido.
          updated_at:   Timestamp de ultima actualizacion (UTC). None si desconocido.
          active_count: Numero de veces que el contexto fue recuperado.
                        Usado por hotness_score() para priorizar resultados frecuentes.
          related_uri:  Lista de URIs de contextos relacionados. Permite grafos de relaciones.
          meta:         Datos arbitrarios adicionales (parametros, etiquetas, etc.).
      """

      id: str
      uri: str
      parent_uri: str | None = None
      is_leaf: bool = False
      abstract: str = ""
      context_type: str = "resource"
      category: str = ""
      level: int | None = None
      created_at: datetime | None = None
      updated_at: datetime | None = None
      active_count: int = 0
      related_uri: list[str] = field(default_factory=list)
      meta: dict[str, Any] = field(default_factory=dict)

      def to_dict(self) -> dict:
          """Serializa el contexto a dict con timestamps en formato ISO 8601.

          Returns:
              dict con todos los campos listos para JSON o almacenamiento en BD.
          """
          return {
              "id": self.id,
              "uri": self.uri,
              "parent_uri": self.parent_uri,
              "is_leaf": self.is_leaf,
              "abstract": self.abstract,
              "context_type": self.context_type,
              "category": self.category,
              "level": self.level,
              "created_at": self.created_at.isoformat() if self.created_at else None,
              "updated_at": self.updated_at.isoformat() if self.updated_at else None,
              "active_count": self.active_count,
              "related_uri": self.related_uri,
              "meta": self.meta,
          }

      @classmethod
      def from_dict(cls, data: dict) -> "Context":
          """Deserializa un Context desde un dict (inverso de to_dict).

          Args:
              data: dict con los campos del contexto. Los timestamps pueden ser
                    strings ISO 8601 o None.

          Returns:
              Instancia de Context con los valores del dict.
          """
          created_at = data.get("created_at")
          updated_at = data.get("updated_at")
          return cls(
              id=data["id"],
              uri=data["uri"],
              parent_uri=data.get("parent_uri"),
              is_leaf=data.get("is_leaf", False),
              abstract=data.get("abstract", ""),
              context_type=data.get("context_type", "resource"),
              category=data.get("category", ""),
              level=data.get("level"),
              created_at=datetime.fromisoformat(created_at) if isinstance(created_at, str) else created_at,
              updated_at=datetime.fromisoformat(updated_at) if isinstance(updated_at, str) else updated_at,
              active_count=data.get("active_count", 0),
              related_uri=data.get("related_uri", []),
              meta=data.get("meta", {}),
          )


id: context_level_py_core
name: context_level
lang: py
domain: core
version: 1.0.0
algebraic: sum
definition:
  class ContextLevel(int, Enum):
  ABSTRACT = 0
  OVERVIEW = 1
  DETAIL = 2

tags: ["context","retrieval","rag","vector-db","hierarchy","level","indexing"]
uses_types: []
file_path: python/types/core/context_level.py
created_at: 2026-04-05T11:20:39Z
updated_at: 2026-04-05T11:20:39Z
content_hash: 6ae453b086e815a280a8fe2653e4fedcfc972ea58c396b7f51275956d2841e41
description:
  Nivel de detalle de un contexto para indexacion vectorial y retrieval jerarquico. El retriever primero busca en L0 (rapido, barato), luego refina con L1/L2. Hereda de int para comparacion ordinal directa.

notes:
  Hereda de `int` ademas de `Enum` para que las variantes sean directamente comparables y ordenables: `ContextLevel.ABSTRACT < ContextLevel.DETAIL`. Util en logica de retrieval que filtra por nivel minimo requerido.

  Los valores enteros mapean a la granularidad del contenido: 0 (mas comprimido) → 2 (mas detallado). El retriever implementa una estrategia de cascada: busca primero en L0, si la similitud supera el umbral devuelve el resultado; si no, sube a L1 o L2.

code:
  """ContextLevel — nivel de detalle de un contexto para retrieval jerarquico."""

  from enum import Enum


  class ContextLevel(int, Enum):
      """Nivel de detalle de un contexto para indexacion vectorial y retrieval jerarquico.

      El retriever primero busca en ABSTRACT (rapido, barato), luego refina con
      OVERVIEW o DETAIL segun la precision requerida.

      Variantes:
          ABSTRACT: L0 — resumen corto (~256 chars). Maximo rendimiento, minimo costo.
          OVERVIEW: L1 — overview del contenido (~4000 chars). Balance costo/precision.
          DETAIL:   L2 — contenido completo. Maximo detalle, mayor costo de retrieval.
      """

      ABSTRACT = 0
      OVERVIEW = 1
      DETAIL = 2


id: context_part_py_core
name: context_part
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["message","part","context","memory","resource","skill","multipart","llm","rag"]
uses_types: []
file_path: python/types/core/context_part.py
created_at: 2026-04-05T11:20:30Z
updated_at: 2026-04-05T11:20:30Z
content_hash: 473562e3f81def5b183bfadd30b97e82e9789609f9b4cbb6feed5dc711d62a44
definition:
  @dataclass
  class ContextPart:
      uri: str = ""
      context_type: str = "memory"
      abstract: str = ""
      type: str = "context"

description:
  Referencia a un contexto externo (memoria, recurso o skill) dentro de un mensaje multipart. Permite enriquecer mensajes con referencias tipadas a contextos recuperados por el agente.

notes:
  `context_type` puede ser "memory" (recuperado de memoria a largo plazo), "resource" (documento o fichero externo), o "skill" (capacidad o herramienta disponible).

  `uri` identifica el recurso de forma unica — puede ser un ID de memoria, una URL, o un nombre de skill.

  `abstract` contiene un resumen legible del contexto, util para estimar tokens sin cargar el contenido completo.

  El campo `type` actua como discriminador literal en el tipo suma `Part`. Siempre vale `"context"`.

code:
  """ContextPart — referencia a un contexto externo en un mensaje multipart."""

  from dataclasses import dataclass, field


  @dataclass
  class ContextPart:
      """Referencia a un contexto externo (memoria, recurso o skill) en un mensaje multipart.

      Attributes:
          uri:          URI que identifica el contexto referenciado.
          context_type: Tipo de contexto: "memory" | "resource" | "skill".
          abstract:     Resumen legible del contenido del contexto.
          type:         Discriminador literal, siempre "context".
      """

      uri: str = ""
      context_type: str = "memory"
      abstract: str = ""
      type: str = field(default="context", init=True)


id: context_type_py_core
name: context_type
lang: py
domain: core
version: 1.0.0
algebraic: sum
definition:
  class ContextType(str, Enum):
  MEMORY = "memory"
  RESOURCE = "resource"
  SKILL = "skill"

tags: ["retrieval","context","enum","rag","search","filter"]
uses_types: []
file_path: python/types/core/context_type.py
created_at: 2026-04-05T11:20:30Z
updated_at: 2026-04-05T11:20:30Z
content_hash: b914a799febb8475fceb279acffe9794723fffda989b829afe9a69a6b3215766
description:
  Tipo de contexto para busqueda/retrieval. Permite filtrar resultados por categoria: memoria de sesion, recursos indexados, o skills/capacidades.

notes:
  Hereda de `str` y `Enum` para que las variantes sean directamente serializables como strings JSON sin necesidad de llamar a `.value`. Esto facilita la interoperabilidad con APIs y almacenamiento.

  Las tres categorias reflejan la arquitectura tipica de sistemas RAG con memoria de agente: memorias de conversacion, documentos/recursos externos, y skills o procedimientos.

code:
  """ContextType — tipo de contexto para busqueda/retrieval."""

  from enum import Enum


  class ContextType(str, Enum):
      """Tipo de contexto para busqueda/retrieval.

      Permite filtrar resultados por categoria al construir queries tipadas.

      Variants:
          MEMORY:   Contexto de memoria (conversaciones, historial de sesion).
          RESOURCE: Contexto de recurso (documentos, archivos, URIs indexados).
          SKILL:    Contexto de skill (instrucciones, capacidades, procedimientos).
      """

      MEMORY = "memory"
      RESOURCE = "resource"
      SKILL = "skill"


id: entity_node_py_core
name: entity_node
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["entity","graph","knowledge-graph","node","core"]
uses_types: []
file_path: python/types/core/entity_node.py
created_at: 2026-04-05T11:18:45Z
updated_at: 2026-04-05T11:18:45Z
content_hash: 430d36ea532ddb32c2b64212b725bcd8441243e55bc82bda26448726a1fc5664
definition:
  @dataclass
  class EntityNode:
      uuid: str
      name: str
      labels: list[str] = field(default_factory=list)
      summary: str = ""
      attributes: dict = field(default_factory=dict)
      related_edges: list[dict] = field(default_factory=list)
      related_nodes: list[dict] = field(default_factory=list)

description:
  Nodo de entidad extraido de un knowledge graph. Contiene la identidad, tipo(s), atributos y relaciones de una entidad. Generico — no acoplado a Zep o ningun graph DB especifico.

examples:
  ```python
  from entity_node import EntityNode

  node = EntityNode(
      uuid="abc-123",
      name="Alan Turing",
      labels=["Person", "Professor"],
      summary="Matematico y pionero de la computacion.",
      attributes={"born": 1912, "nationality": "British"},
  )

  print(node.get_entity_type())  # "Person"
  print(node.to_dict())
  ```

notes:
  El campo `labels` puede contener multiples etiquetas de tipo (e.g., ["Person", "Professor"]). `get_entity_type()` retorna el primer label no-generico para usarlo como tipo principal de la entidad. Los campos `related_edges` y `related_nodes` almacenan relaciones como dicts crudos para maxima flexibilidad sin acoplamiento a un schema especifico.

documentation:
  ## Metodos

  - `to_dict() -> dict` — serializa el nodo a un diccionario con todos sus campos.
  - `get_entity_type() -> str | None` — retorna el primer label que no sea "Entity", o None si todos son genericos.

code:
  """EntityNode — nodo de entidad extraido de un knowledge graph."""

  from dataclasses import dataclass, field


  @dataclass
  class EntityNode:
      """Nodo de entidad extraido de un knowledge graph.

      Contiene la identidad, tipo(s), atributos y relaciones de una entidad.
      Generico — no acoplado a Zep o ningun graph DB especifico.
      """

      uuid: str
      name: str
      labels: list[str] = field(default_factory=list)
      summary: str = ""
      attributes: dict = field(default_factory=dict)
      related_edges: list[dict] = field(default_factory=list)
      related_nodes: list[dict] = field(default_factory=list)

      def to_dict(self) -> dict:
          """Serializa el nodo a un diccionario."""
          return {
              "uuid": self.uuid,
              "name": self.name,
              "labels": self.labels,
              "summary": self.summary,
              "attributes": self.attributes,
              "related_edges": self.related_edges,
              "related_nodes": self.related_nodes,
          }

      def get_entity_type(self) -> str | None:
          """Retorna el primer label que no sea generico ("Entity"), o None."""
          for label in self.labels:
              if label != "Entity":
                  return label
          return None


id: field_type_py_core
name: field_type
lang: py
domain: core
version: 1.0.0
algebraic: sum
tags: ["memory","field","type","enum","schema"]
uses_types: []
file_path: python/types/core/field_type.py
created_at: 2026-04-05T11:20:30Z
updated_at: 2026-04-05T11:20:30Z
content_hash: 2f3f791d3997ac2d39cca02e3b90b0b6d57ea1bec1d3185bf50d054ea53a99e9
definition:
  class FieldType(str, Enum):
      STRING = "string"
      INTEGER = "integer"
      FLOAT = "float"
      BOOLEAN = "boolean"
      LIST = "list"
      DICT = "dict"
      DATETIME = "datetime"

description:
  Tipo de dato de un campo de memoria. Enum sum type con siete variantes que cubren los tipos primitivos y compuestos mas comunes para campos de MemoryField.

notes:
  Hereda de `str` para que los valores sean directamente serializables a JSON sin conversion adicional.
  Usado por `MemoryField` para declarar el tipo de dato esperado en cada campo de un schema de memoria.

code:
  """FieldType — tipo de dato de un campo de memoria."""

  from enum import Enum


  class FieldType(str, Enum):
      """Tipo de dato de un campo de memoria.

      Valores posibles:
          STRING   — cadena de texto.
          INTEGER  — numero entero.
          FLOAT    — numero de punto flotante.
          BOOLEAN  — valor booleano.
          LIST     — lista de valores.
          DICT     — diccionario clave-valor.
          DATETIME — fecha y hora.
      """

      STRING = "string"
      INTEGER = "integer"
      FLOAT = "float"
      BOOLEAN = "boolean"
      LIST = "list"
      DICT = "dict"
      DATETIME = "datetime"


id: filtered_entities_py_core
name: filtered_entities
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["entity","graph","knowledge-graph","filter","core"]
uses_types: ["entity_node_py_core"]
file_path: python/types/core/filtered_entities.py
created_at: 2026-04-05T11:18:49Z
updated_at: 2026-04-05T11:18:49Z
content_hash: 9b6c3eb42b337ade2de49b1cdfe8f2774c95aceec260e90340e62c6730998c89
definition:
  @dataclass
  class FilteredEntities:
      entities: list[EntityNode]
      entity_types: set[str]
      total_count: int
      filtered_count: int

description:
  Resultado de filtrar entidades de un knowledge graph por tipos definidos. Captura tanto las entidades filtradas como los conteos para reporting.

examples:
  ```python
  from entity_node import EntityNode
  from filtered_entities import FilteredEntities

  nodes = [
      EntityNode(uuid="1", name="Alan Turing", labels=["Person"]),
      EntityNode(uuid="2", name="MIT", labels=["Organization"]),
  ]

  result = FilteredEntities(
      entities=nodes,
      entity_types={"Person", "Organization"},
      total_count=10,
      filtered_count=2,
  )

  print(result.filtered_count)  # 2
  print(result.to_dict())
  ```

notes:
  El campo `entity_types` es un `set[str]` que contiene los tipos unicos encontrados entre las entidades filtradas. `total_count` refleja el total antes de aplicar el filtro y `filtered_count` el total despues, permitiendo calcular cuantas entidades fueron descartadas. Depende de `EntityNode` para la lista de entidades.

documentation:
  ## Metodos

  - `to_dict() -> dict` — serializa el resultado a un diccionario, incluyendo cada entidad serializada con `EntityNode.to_dict()`.

code:
  """FilteredEntities — resultado de filtrar entidades de un knowledge graph por tipos."""

  from dataclasses import dataclass, field

  from entity_node import EntityNode


  @dataclass
  class FilteredEntities:
      """Resultado de filtrar entidades de un graph por tipos definidos.

      Captura tanto las entidades filtradas como los conteos para reporting.
      El total_count vs filtered_count muestra cuantas entidades se descartaron.
      """

      entities: list[EntityNode]
      entity_types: set[str]
      total_count: int
      filtered_count: int

      def to_dict(self) -> dict:
          """Serializa el resultado a un diccionario."""
          return {
              "entities": [e.to_dict() for e in self.entities],
              "entity_types": list(self.entity_types),
              "total_count": self.total_count,
              "filtered_count": self.filtered_count,
          }


id: find_result_py_core
name: find_result
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["retrieval","result","rag","search","find","aggregation"]
uses_types: ["matched_context_py_core","query_plan_py_core","query_result_py_core"]
file_path: python/types/core/find_result.py
created_at: 2026-04-05T11:22:16Z
updated_at: 2026-04-05T11:22:16Z
content_hash: 691a83e7857f459e9a3a16bcee5feee345c8a128115e6fea08b1e6d4b9e0bc76
definition:
  @dataclass
  class FindResult:
      memories: list[MatchedContext]
      resources: list[MatchedContext]
      skills: list[MatchedContext]
      query_plan: QueryPlan | None = None
      query_results: list[QueryResult] | None = None
      total: int = 0

description:
  Resultado final de una busqueda completa, agrupado por tipo de contexto. Opcionalmente incluye provenance (traza de busqueda) para observabilidad.

notes:
  `__post_init__` calcula `total` automaticamente al construir la instancia. Si se modifica `memories`, `resources` o `skills` despues de la construccion, `total` no se actualiza automaticamente — se debe reconstruir o actualizar manualmente.

  `__iter__` permite iterar sobre todos los contextos en orden: memories → resources → skills. Facilita patrones como `for ctx in result: process(ctx)` sin necesidad de concatenar las listas.

  `to_dict(include_provenance=True)` incluye el query_plan y un resumen de los query_results. El provenance es util para debugging y observabilidad pero puede ser verboso — por eso es opt-in.

  `from_dict` reconstruye el resultado desde un dict serializado. Los campos de provenance (query_plan, query_results) no se reconstruyen — quedan como None. Si se necesita provenance en la deserializacion, hay que reconstruirlos separadamente.

code:
  """FindResult — resultado final de una busqueda completa agrupado por tipo de contexto."""

  from __future__ import annotations

  from dataclasses import dataclass, field
  from typing import Iterator

  from matched_context import MatchedContext
  from query_plan import QueryPlan
  from query_result import QueryResult


  @dataclass
  class FindResult:
      """Resultado final de una busqueda completa, agrupado por tipo de contexto.

      Opcionalmente incluye provenance (traza de busqueda) para observabilidad.

      Attributes:
          memories:       Contextos encontrados de tipo MEMORY.
          resources:      Contextos encontrados de tipo RESOURCE.
          skills:         Contextos encontrados de tipo SKILL.
          query_plan:     Plan de busqueda que genero estos resultados (provenance).
          query_results:  Resultados individuales por TypedQuery (provenance).
          total:          Total de contextos encontrados (calculado en __post_init__).
      """

      memories: list[MatchedContext] = field(default_factory=list)
      resources: list[MatchedContext] = field(default_factory=list)
      skills: list[MatchedContext] = field(default_factory=list)
      query_plan: QueryPlan | None = None
      query_results: list[QueryResult] | None = None
      total: int = 0

      def __post_init__(self) -> None:
          """Calcula el total de contextos encontrados."""
          self.total = len(self.memories) + len(self.resources) + len(self.skills)

      def __iter__(self) -> Iterator[MatchedContext]:
          """Itera sobre todos los MatchedContext (memories + resources + skills)."""
          yield from self.memories
          yield from self.resources
          yield from self.skills

      def to_dict(self, include_provenance: bool = False) -> dict:
          """Serializa el resultado a dict.

          Args:
              include_provenance: Si True, incluye query_plan y query_results en el output.

          Returns:
              dict con memories, resources, skills y total. Opcionalmente con provenance.
          """

          def _context_to_dict(ctx: MatchedContext) -> dict:
              return {
                  "uri": ctx.uri,
                  "context_type": ctx.context_type.value,
                  "level": ctx.level,
                  "abstract": ctx.abstract,
                  "overview": ctx.overview,
                  "category": ctx.category,
                  "score": ctx.score,
                  "match_reason": ctx.match_reason,
                  "relations": [
                      {"uri": r.uri, "abstract": r.abstract} for r in ctx.relations
                  ],
              }

          result: dict = {
              "memories": [_context_to_dict(c) for c in self.memories],
              "resources": [_context_to_dict(c) for c in self.resources],
              "skills": [_context_to_dict(c) for c in self.skills],
              "total": self.total,
          }

          if include_provenance and self.query_plan is not None:
              result["query_plan"] = {
                  "session_context": self.query_plan.session_context,
                  "reasoning": self.query_plan.reasoning,
                  "queries": [
                      {
                          "query": q.query,
                          "context_type": q.context_type.value if q.context_type else None,
                          "intent": q.intent,
                          "priority": q.priority,
                          "target_directories": q.target_directories,
                      }
                      for q in self.query_plan.queries
                  ],
              }

          if include_provenance and self.query_results is not None:
              result["query_results"] = [
                  {
                      "query": qr.query.query,
                      "matched_count": len(qr.matched_contexts),
                      "searched_directories": qr.searched_directories,
                  }
                  for qr in self.query_results
              ]

          return result

      @classmethod
      def from_dict(cls, data: dict) -> FindResult:
          """Deserializa un FindResult desde un dict (tipicamente JSON parseado).

          Args:
              data: dict con estructura equivalente a la producida por to_dict().

          Returns:
              FindResult reconstruido. Los campos de provenance se omiten (None).
          """
          from context_type import ContextType
          from related_context import RelatedContext

          def _dict_to_context(d: dict) -> MatchedContext:
              return MatchedContext(
                  uri=d["uri"],
                  context_type=ContextType(d["context_type"]),
                  level=d.get("level", 2),
                  abstract=d.get("abstract", ""),
                  overview=d.get("overview"),
                  category=d.get("category", ""),
                  score=d.get("score", 0.0),
                  match_reason=d.get("match_reason", ""),
                  relations=[
                      RelatedContext(uri=r["uri"], abstract=r.get("abstract", ""))
                      for r in d.get("relations", [])
                  ],
              )

          return cls(
              memories=[_dict_to_context(c) for c in data.get("memories", [])],
              resources=[_dict_to_context(c) for c in data.get("resources", [])],
              skills=[_dict_to_context(c) for c in data.get("skills", [])],
          )


id: matched_context_py_core
name: matched_context
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["retrieval","context","rag","match","score","result"]
uses_types: ["context_type_py_core","related_context_py_core"]
file_path: python/types/core/matched_context.py
created_at: 2026-04-05T11:21:19Z
updated_at: 2026-04-05T11:21:19Z
content_hash: d096356769455328d9e6517c6bc2f21731aeca78c0f887dfd8d872a8070c7a77
definition:
  @dataclass
  class MatchedContext:
      uri: str
      context_type: ContextType
      level: int = 2
      abstract: str = ""
      overview: str | None = None
      category: str = ""
      score: float = 0.0
      match_reason: str = ""
      relations: list[RelatedContext] = field(default_factory=list)

description:
  Contexto encontrado por el retriever con su score de relevancia, nivel de detalle, y contextos relacionados.

notes:
  El campo `level` define cuanto contenido fue cargado: 0 solo tiene `abstract`, 1 tiene `abstract` + `overview`, 2 tiene el contenido completo. El retriever puede usar niveles bajos para hacer un primer filtrado barato y luego cargar el nivel 2 solo para los candidatos seleccionados.

  `score` en rango 0.0-1.0 donde 1.0 es maxima relevancia. El umbral de inclusion depende de la estrategia del retriever (ver `ScoreDistribution` para calcular umbrales adaptativos).

  `match_reason` es texto libre generado por el LLM o el retriever para explicar la decision. Util para debugging y para mostrar al usuario por que se incluyo este contexto.

  `relations` permite construir grafos de conocimiento ligeros sin un graph DB completo — cada contexto conoce sus vecinos directos.

code:
  """MatchedContext — contexto encontrado por el retriever con score y relaciones."""

  from __future__ import annotations

  from dataclasses import dataclass, field

  from context_type import ContextType
  from related_context import RelatedContext


  @dataclass
  class MatchedContext:
      """Contexto encontrado por el retriever con score de relevancia y relaciones.

      Modela un resultado de busqueda con niveles de detalle progresivos:
      abstract (minimo), overview (resumen), y detail (contenido completo).

      Attributes:
          uri:          Identificador unico del contexto.
          context_type: Categoria del contexto (MEMORY, RESOURCE, SKILL).
          level:        Nivel de detalle cargado: 0=abstract, 1=overview, 2=detail.
          abstract:     Resumen breve siempre disponible.
          overview:     Resumen extendido, disponible en level >= 1.
          category:     Subcategoria libre dentro del context_type.
          score:        Score de relevancia 0.0-1.0.
          match_reason: Explicacion textual del por que este contexto fue seleccionado.
          relations:    Contextos relacionados (referencias cruzadas).
      """

      uri: str
      context_type: ContextType
      level: int = 2
      abstract: str = ""
      overview: str | None = None
      category: str = ""
      score: float = 0.0
      match_reason: str = ""
      relations: list[RelatedContext] = field(default_factory=list)


id: memory_data_py_core
name: memory_data
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["memory","data","agent","dataclass","retrieval","hierarchical"]
uses_types: ["memory_type_schema_py_core"]
file_path: python/types/core/memory_data.py
created_at: 2026-04-05T11:21:23Z
updated_at: 2026-04-05T11:21:23Z
content_hash: 02e6453d71d1ab54a3cf70b7add62709647ffa577428fc4e081f572af8b760d6
definition:
  @dataclass
  class MemoryData:
      memory_type: str
      uri: str | None = None
      fields: dict[str, Any] = field(default_factory=dict)
      abstract: str | None = None
      overview: str | None = None
      content: str | None = None
      name: str | None = None
      tags: list[str] = field(default_factory=list)
      created_at: datetime | None = None
      updated_at: datetime | None = None

description:
  Instancia concreta de un dato de memoria de agente. Los campos son dinamicos (dict) validados contra MemoryTypeSchema. Soporta 3 niveles de contenido (L0 abstract, L1 overview, L2 content) para retrieval jerarquico eficiente.

examples:
  ```python
  entry = MemoryData(
      memory_type="profile",
      uri="profile/lucasg",
      name="Lucas G",
      abstract="Desarrollador senior con foco en sistemas distribuidos",
      fields={
          "username": "lucasg",
          "role": "senior engineer",
          "interaction_count": 42,
      },
      tags=["active", "admin"],
  )

  entry.set_field("role", "tech lead")
  role = entry.get_field("role")  # "tech lead"
  ```

notes:
  El campo `fields` es un dict dinamico cuyas claves deben corresponder a los `name` de los `MemoryField` del `MemoryTypeSchema` asociado. La validacion contra el schema es responsabilidad del sistema que consume este tipo.

  Los 3 niveles de contenido permiten retrieval jerarquico:
  - `abstract` (L0): una linea, para mostrar en listas o contexto muy limitado.
  - `overview` (L1): parrafo, para contexto medio sin cargar el contenido completo.
  - `content` (L2): sin limite, el dato completo serializado.

  `uri` identifica la instancia para operaciones de update. Si es `None`, se trata como nueva insercion.

  Metodos auxiliares `get_field` y `set_field` proveen acceso tipado al dict `fields`.

code:
  """MemoryData — instancia concreta de un dato de memoria de agente."""

  from __future__ import annotations

  from dataclasses import dataclass, field
  from datetime import datetime
  from typing import Any


  @dataclass
  class MemoryData:
      """Instancia concreta de un dato de memoria.

      Los campos son dinamicos (dict) y se validan contra el MemoryTypeSchema
      correspondiente. Soporta 3 niveles de contenido para retrieval jerarquico:
          L0 abstract  — resumen corto (una linea).
          L1 overview  — resumen medio (parrafo).
          L2 content   — contenido completo (sin limite).

      Attributes:
          memory_type: tipo de memoria al que pertenece esta instancia.
          uri:         URI para identificar y localizar la instancia en updates.
          fields:      datos dinamicos del schema correspondiente.
          abstract:    L0 resumen corto para retrieval rapido.
          overview:    L1 resumen medio para contexto.
          content:     L2 contenido completo.
          name:        nombre legible de la instancia.
          tags:        etiquetas para filtrado y busqueda.
          created_at:  timestamp de creacion.
          updated_at:  timestamp de ultima actualizacion.
      """

      memory_type: str
      uri: str | None = None
      fields: dict[str, Any] = field(default_factory=dict)
      abstract: str | None = None
      overview: str | None = None
      content: str | None = None
      name: str | None = None
      tags: list[str] = field(default_factory=list)
      created_at: datetime | None = None
      updated_at: datetime | None = None

      def get_field(self, field_name: str) -> Any:
          """Retorna el valor de un campo dinamico por nombre.

          Args:
              field_name: nombre del campo a obtener.

          Returns:
              El valor del campo, o None si no existe.
          """
          return self.fields.get(field_name)

      def set_field(self, field_name: str, value: Any) -> None:
          """Establece el valor de un campo dinamico.

          Args:
              field_name: nombre del campo a establecer.
              value:      valor a asignar.
          """
          self.fields[field_name] = value


id: memory_field_py_core
name: memory_field
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["memory","field","schema","dataclass"]
uses_types: ["field_type_py_core","merge_op_py_core"]
file_path: python/types/core/memory_field.py
created_at: 2026-04-05T11:20:46Z
updated_at: 2026-04-05T11:20:46Z
content_hash: bdec74e005ca3871faf7cfa472044f602c5f8af11ae004de7f4afe77016ea528
definition:
  @dataclass
  class MemoryField:
      name: str
      field_type: FieldType
      description: str = ""
      merge_op: MergeOp = MergeOp.PATCH

description:
  Definicion de un campo dentro de un tipo de memoria. El merge_op determina como se actualizan valores existentes: PATCH para cambios parciales, SUM para acumulacion numerica, IMMUTABLE para proteger el valor original.

examples:
  ```python
  MemoryField(
      name="username",
      field_type=FieldType.STRING,
      description="Nombre de usuario del sistema",
      merge_op=MergeOp.IMMUTABLE,
  )
  ```

notes:
  Dataclass inmutable por convencion (no frozen para permitir construccion incremental).
  El campo `description` es consumido por el LLM para entender que dato debe colocar en cada campo.
  Usado por `MemoryTypeSchema` como elemento de la lista `fields`.

code:
  """MemoryField — definicion de un campo dentro de un tipo de memoria."""

  from dataclasses import dataclass, field

  from field_type import FieldType
  from merge_op import MergeOp


  @dataclass
  class MemoryField:
      """Definicion de un campo dentro de un tipo de memoria.

      Attributes:
          name:        nombre del campo.
          field_type:  tipo de dato del campo.
          description: descripcion para el LLM sobre el proposito del campo.
          merge_op:    estrategia de merge cuando se actualiza un valor existente.
      """

      name: str
      field_type: FieldType
      description: str = ""
      merge_op: MergeOp = MergeOp.PATCH


id: memory_type_schema_py_core
name: memory_type_schema
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["memory","schema","agent","dataclass","filesystem"]
uses_types: ["memory_field_py_core"]
file_path: python/types/core/memory_type_schema.py
created_at: 2026-04-05T11:21:03Z
updated_at: 2026-04-05T11:21:03Z
content_hash: ccc952799eddbd940a3f4460cef8d17b88d8c91cf5c6e00cc7e5acba57b072ea
definition:
  @dataclass
  class MemoryTypeSchema:
      memory_type: str
      description: str = ""
      fields: list[MemoryField] = field(default_factory=list)
      filename_template: str = ""
      directory: str = ""
      enabled: bool = True
      operation_mode: str = "upsert"

description:
  Schema de un tipo de memoria de agente. Define la estructura de los datos, la estrategia de merge por campo y como se organizan en el filesystem. Soporta modos upsert, add_only y update_only.

examples:
  ```python
  MemoryTypeSchema(
      memory_type="profile",
      description="Informacion persistente del usuario",
      fields=[
          MemoryField("username", FieldType.STRING, "Nombre de usuario", MergeOp.IMMUTABLE),
          MemoryField("role", FieldType.STRING, "Rol en el equipo", MergeOp.PATCH),
          MemoryField("interaction_count", FieldType.INTEGER, "Total de interacciones", MergeOp.SUM),
      ],
      filename_template="{username}.json",
      directory="memory/profile",
      operation_mode="upsert",
  )
  ```

notes:
  `operation_mode` controla la semantica de escritura:
  - `upsert`: crear si no existe, actualizar si existe (comportamiento default).
  - `add_only`: solo crea nuevas entradas; si ya existe una con la misma URI, no la toca.
  - `update_only`: solo actualiza entradas existentes; si no existe, descarta la operacion.

  `filename_template` puede contener placeholders como `{name}` o `{date}` para generar nombres de archivo dinamicamente.

  Categorias de memoria predefinidas recomendadas:
  - `profile` — informacion del usuario (nombre, rol, expertise)
  - `preferences` — preferencias (idioma, estilo de respuesta)
  - `entities` — entidades conocidas (personas, proyectos, tools)
  - `events` — eventos ocurridos (reuniones, deployments, incidentes)
  - `cases` — casos/precedentes (bugs resueltos, decisiones)
  - `patterns` — patrones observados (workflows, preferencias recurrentes)
  - `tools` — herramientas conocidas (comandos, APIs, shortcuts)
  - `skills` — habilidades/capacidades del agente o del usuario

code:
  """MemoryTypeSchema — schema de un tipo de memoria de agente."""

  from dataclasses import dataclass, field

  from memory_field import MemoryField


  @dataclass
  class MemoryTypeSchema:
      """Schema de un tipo de memoria.

      Define la estructura de los datos, la estrategia de merge por campo,
      y como se organizan en el filesystem. Soporta 3 modos de operacion:
          upsert      — crear o actualizar (default).
          add_only    — solo crear, nunca actualizar existentes.
          update_only — solo actualizar existentes, nunca crear nuevos.

      Attributes:
          memory_type:       nombre del tipo (ej: "profile", "preferences").
          description:       descripcion del tipo para el LLM.
          fields:            lista de campos que define la estructura.
          filename_template: template para el nombre de archivo al persistir.
          directory:         directorio donde se almacenan las instancias.
          enabled:           si el tipo esta activo.
          operation_mode:    modo de operacion ("upsert" | "add_only" | "update_only").
      """

      memory_type: str
      description: str = ""
      fields: list[MemoryField] = field(default_factory=list)
      filename_template: str = ""
      directory: str = ""
      enabled: bool = True
      operation_mode: str = "upsert"


id: merge_op_py_core
name: merge_op
lang: py
domain: core
version: 1.0.0
algebraic: sum
definition:
  class MergeOp(str, Enum):
  PATCH = "patch"
  SUM = "sum"
  IMMUTABLE = "immutable"

tags: ["memory","merge","strategy","enum","schema"]
uses_types: []
file_path: python/types/core/merge_op.py
created_at: 2026-04-05T11:20:39Z
updated_at: 2026-04-05T11:20:39Z
content_hash: 64f8a5f6fcd47db7b8d2ba0ad05f616fe60618b00355a73d760806e81fdff75e
description:
  Estrategia de merge cuando se actualiza un campo de memoria existente. PATCH aplica cambios parciales (search/replace en strings), SUM acumula valores numericos, IMMUTABLE protege contra modificacion.

notes:
  Hereda de `str` para serializacion JSON directa.
  Usado por `MemoryField` como valor default `MergeOp.PATCH`.
  - `PATCH`: para strings aplica search/replace; para otros tipos reemplaza directamente.
  - `SUM`: acumula numeros; util para contadores de eventos o frecuencias.
  - `IMMUTABLE`: protege campos que no deben cambiar una vez establecidos (ej: fecha de creacion, ID).

code:
  """MergeOp — estrategia de merge para campos de memoria."""

  from enum import Enum


  class MergeOp(str, Enum):
      """Estrategia de merge cuando se actualiza un campo de memoria existente.

      Valores posibles:
          PATCH     — aplica cambios parciales (search/replace en strings,
                      replace directo para otros tipos).
          SUM       — acumula valores numericos (contadores).
          IMMUTABLE — protege el campo contra modificacion despues de creado.
      """

      PATCH = "patch"
      SUM = "sum"
      IMMUTABLE = "immutable"


id: message_py_core
name: message
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["message","conversation","multipart","llm","agent","history","jsonl","serialization","tokens"]
uses_types: ["part_py_core","text_part_py_core","context_part_py_core","tool_part_py_core"]
file_path: python/types/core/message.py
created_at: 2026-04-05T11:21:35Z
updated_at: 2026-04-05T11:21:35Z
content_hash: 0fc1dd8991fdc81e5aaf7945f8fd47d4273e75a3bd88798d1f32127dedeb7175
definition:
  @dataclass
  class Message:
      id: str
      role: str
      parts: list[Part]
      created_at: datetime | None = None

description:
  Modelo de mensaje multipart para conversaciones con agentes. Soporta texto, referencias a contexto (memoria/recurso/skill) y llamadas a herramientas en un mensaje unificado. Disenado para ser serializable a JSONL para persistencia.

notes:
  El campo `role` acepta "user" o "assistant" por convencion, pero no se valida — el tipo es abierto para compatibilidad con futuros roles (e.g., "system", "tool").

  La estimacion de tokens (ceil(chars / 4)) es una heuristica. No sustituye a un tokenizador real, pero es util para presupuesto rapido sin dependencias externas.

  `to_dict()` usa `dataclasses.asdict()` sobre cada Part, que serializa recursivamente incluyendo el campo `type` discriminador — esto garantiza que `from_dict()` puede reconstruir el tipo correcto.

  Las factories `create_user` y `create_assistant` generan IDs con `uuid.uuid4()` y timestamps UTC automaticamente, simplificando la construccion en pipelines de conversacion.

documentation:
  ## Metodos

  - `content` (property) — texto del primer `TextPart`. Vacio si no hay TextPart.
  - `estimated_tokens` (property) — estimacion `ceil(total_chars / 4)`. Cuenta chars de TextParts, abstracts de ContextParts, y JSON de tool_input + tool_output de ToolParts.
  - `to_dict() -> dict` — serializa a dict listo para `json.dumps()`. Timestamps en ISO 8601.
  - `to_jsonl() -> str` — serializa a string JSON de una sola linea.
  - `from_dict(data: dict) -> Message` (classmethod) — deserializa usando `part_from_dict()` para cada parte.
  - `create_user(content: str) -> Message` (classmethod) — factory: role="user", un TextPart, UUID y timestamp UTC automaticos.
  - `create_assistant(content, context_refs=None, tool_calls=None) -> Message` (classmethod) — factory: role="assistant", orden de partes: ContextParts → TextPart → ToolParts.
  - `get_context_parts() -> list[ContextPart]` — filtra partes de tipo ContextPart.
  - `get_tool_parts() -> list[ToolPart]` — filtra partes de tipo ToolPart.
  - `find_tool_part(tool_id: str) -> ToolPart | None` — busca un ToolPart por tool_id.

code:
  """Message — modelo de mensaje multipart para conversaciones con agentes."""

  import dataclasses
  import json
  import math
  import uuid
  from dataclasses import dataclass, field
  from datetime import datetime, timezone
  from typing import ClassVar

  from context_part import ContextPart
  from part import Part, part_from_dict
  from text_part import TextPart
  from tool_part import ToolPart


  @dataclass
  class Message:
      """Modelo de mensaje multipart para conversaciones con agentes.

      Soporta texto, referencias a contexto (memoria/recurso/skill) y llamadas
      a herramientas en un mensaje unificado. Disenado para ser serializable a
      JSONL para persistencia.

      Attributes:
          id:         Identificador unico del mensaje.
          role:       Rol del emisor: "user" | "assistant".
          parts:      Lista de partes del mensaje (TextPart, ContextPart, ToolPart).
          created_at: Timestamp de creacion en UTC.
      """

      id: str
      role: str
      parts: list[Part]
      created_at: datetime | None = None

      # --- Properties ---

      @property
      def content(self) -> str:
          """Texto del primer TextPart del mensaje. Vacio si no hay TextPart."""
          for part in self.parts:
              if isinstance(part, TextPart):
                  return part.text
          return ""

      @property
      def estimated_tokens(self) -> int:
          """Estimacion de tokens consumidos por el mensaje.

          Cuenta caracteres de TextParts, abstracts de ContextParts, e inputs/outputs
          de ToolParts. Divide entre 4 (heuristica estandar: ~4 chars por token).

          Returns:
              Numero entero de tokens estimados (ceil).
          """
          total_chars = 0
          for part in self.parts:
              if isinstance(part, TextPart):
                  total_chars += len(part.text)
              elif isinstance(part, ContextPart):
                  total_chars += len(part.abstract)
              elif isinstance(part, ToolPart):
                  if part.tool_input:
                      total_chars += len(json.dumps(part.tool_input))
                  total_chars += len(part.tool_output)
          return math.ceil(total_chars / 4)

      # --- Serialization ---

      def to_dict(self) -> dict:
          """Serializa el mensaje a un dict compatible con JSONL.

          Returns:
              Dict con todos los campos del mensaje listos para json.dumps().
          """
          return {
              "id": self.id,
              "role": self.role,
              "parts": [dataclasses.asdict(p) for p in self.parts],
              "created_at": self.created_at.isoformat() if self.created_at else None,
          }

      def to_jsonl(self) -> str:
          """Serializa el mensaje a un string JSON en una sola linea (JSONL).

          Returns:
              String JSON sin saltos de linea internos.
          """
          return json.dumps(self.to_dict(), ensure_ascii=False)

      @classmethod
      def from_dict(cls, data: dict) -> "Message":
          """Deserializa un Message desde un dict.

          Args:
              data: Dict con los campos del mensaje. Los dicts de 'parts'
                    se deserializan con part_from_dict().

          Returns:
              Instancia de Message reconstruida.
          """
          parts = [part_from_dict(p) for p in data.get("parts", [])]
          created_at = None
          if data.get("created_at"):
              created_at = datetime.fromisoformat(data["created_at"])
          return cls(
              id=data["id"],
              role=data["role"],
              parts=parts,
              created_at=created_at,
          )

      # --- Factories ---

      @classmethod
      def create_user(cls, content: str) -> "Message":
          """Crea un mensaje de usuario con un unico TextPart.

          Args:
              content: Texto del mensaje.

          Returns:
              Message con role="user" y un TextPart.
          """
          return cls(
              id=str(uuid.uuid4()),
              role="user",
              parts=[TextPart(text=content)],
              created_at=datetime.now(timezone.utc),
          )

      @classmethod
      def create_assistant(
          cls,
          content: str,
          context_refs: list[ContextPart] | None = None,
          tool_calls: list[ToolPart] | None = None,
      ) -> "Message":
          """Crea un mensaje de asistente con texto, contextos opcionales y tool calls opcionales.

          El orden de las partes es: ContextParts, TextPart, ToolParts.

          Args:
              content:      Texto principal de la respuesta del asistente.
              context_refs: Referencias a contextos que el asistente uso para responder.
              tool_calls:   Llamadas a herramientas realizadas por el asistente.

          Returns:
              Message con role="assistant" y las partes compuestas.
          """
          parts: list[Part] = []
          if context_refs:
              parts.extend(context_refs)
          parts.append(TextPart(text=content))
          if tool_calls:
              parts.extend(tool_calls)
          return cls(
              id=str(uuid.uuid4()),
              role="assistant",
              parts=parts,
              created_at=datetime.now(timezone.utc),
          )

      # --- Accessors ---

      def get_context_parts(self) -> list[ContextPart]:
          """Devuelve todas las partes de tipo ContextPart del mensaje.

          Returns:
              Lista (posiblemente vacia) de ContextParts.
          """
          return [p for p in self.parts if isinstance(p, ContextPart)]

      def get_tool_parts(self) -> list[ToolPart]:
          """Devuelve todas las partes de tipo ToolPart del mensaje.

          Returns:
              Lista (posiblemente vacia) de ToolParts.
          """
          return [p for p in self.parts if isinstance(p, ToolPart)]

      def find_tool_part(self, tool_id: str) -> ToolPart | None:
          """Busca un ToolPart por su tool_id.

          Args:
              tool_id: Identificador de la invocacion de la herramienta.

          Returns:
              El ToolPart encontrado, o None si no existe.
          """
          for part in self.parts:
              if isinstance(part, ToolPart) and part.tool_id == tool_id:
                  return part
          return None


id: node_type_py_core
name: node_type
lang: py
domain: core
version: 1.0.0
algebraic: sum
definition:
  class NodeType(str, Enum):
  ROOT = "root"
  SECTION = "section"

tags: ["parsing","document","tree","enum","node","core"]
uses_types: []
file_path: python/types/core/node_type.py
created_at: 2026-04-05T11:21:03Z
updated_at: 2026-04-05T11:21:03Z
content_hash: d46f3f559c1ca9b81e41a4eb65e1a361c3897967f9bcdd69b8a6818a4dc7272a
description:
  Tipos de nodo en un arbol de documento parseado. Solo ROOT y SECTION — todo el contenido detallado permanece como markdown dentro del campo content del ResourceNode.

examples:
  ```python
  from node_type import NodeType

  print(NodeType.ROOT.value)     # "root"
  print(NodeType.SECTION.value)  # "section"

  node_type = NodeType("section")
  print(node_type == NodeType.SECTION)  # True
  ```

notes:
  Diseno intencionalmente simple. Evita la decomposicion fina de nodos (no hay PARAGRAPH, CODE_BLOCK, TABLE, LIST, etc.) para mantener el arbol manejable y el contenido preservado en formato markdown. Hereda de `str` para serializar directamente a JSON sin conversion manual.

documentation:
  ## Valores

  - `ROOT` — nodo raiz del arbol, unico por documento.
  - `SECTION` — seccion o subseccion del documento (cualquier nivel).

code:
  """NodeType — tipos de nodo en un arbol de documento parseado."""

  from enum import Enum


  class NodeType(str, Enum):
      """Tipos de nodo en un arbol de documento parseado.

      Valores posibles:
          ROOT    — nodo raiz del arbol, unico por documento.
          SECTION — seccion o subseccion del documento.

      Diseno intencionalmente simple: no hay PARAGRAPH, CODE_BLOCK, TABLE, etc.
      Todo el contenido detallado permanece como markdown dentro del campo
      content del ResourceNode.
      """

      ROOT = "root"
      SECTION = "section"


id: parse_result_py_core
name: parse_result
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["parsing","document","result","tree","pipeline","core"]
uses_types: ["resource_node_py_core","node_type_py_core"]
file_path: python/types/core/parse_result.py
created_at: 2026-04-05T11:21:25Z
updated_at: 2026-04-05T11:21:25Z
content_hash: b3262f47068d0371695df97907a27e5a7c5e9aca1495399cc5df8f882c7a0663
definition:
  @dataclass
  class ParseResult:
      root: ResourceNode
      source_path: str | None = None
      source_format: str | None = None
      parser_name: str | None = None
      parser_version: str | None = None
      parse_time: float | None = None
      parse_timestamp: datetime | None = None
      meta: dict[str, Any] = field(default_factory=dict)
      warnings: list[str] = field(default_factory=list)

description:
  Resultado completo de parsear un documento. Contiene el arbol de nodos, metadata del proceso y warnings. Es el tipo de retorno unificado de todos los parsers del registry.

examples:
  ```python
  import time
  from node_type import NodeType
  from resource_node import ResourceNode
  from parse_result import ParseResult, create_parse_result

  root = ResourceNode(type=NodeType.ROOT, title="Documento")
  intro = ResourceNode(type=NodeType.SECTION, title="Intro", level=1, content="...")
  root.add_child(intro)

  result = create_parse_result(
      root=root,
      source_path="/docs/mi_doc.md",
      source_format="markdown",
      parser_name="MarkdownParser",
      parse_time=0.042,
  )

  print(result.success)          # True (sin warnings)
  print(len(result.get_all_nodes()))   # 2 (root + intro)
  print(len(result.get_sections()))    # 1 (solo intro, root es ROOT)
  print(result.get_sections(min_level=1, max_level=1))  # [intro]
  ```

notes:
  El tipo de retorno unificado permite que los consumidores (pipelines, agentes, APIs) trabajen con el mismo contrato independientemente del formato de origen. El campo `warnings` captura problemas no fatales (e.g., encoding issues, headings malformados) sin lanzar excepciones. `get_all_nodes()` usa BFS para preservar el orden natural de lectura del documento.

documentation:
  ## Propiedades y metodos

  - `success -> bool` (property) — True si `warnings` esta vacio.
  - `get_all_nodes() -> list[ResourceNode]` — flatten BFS del arbol completo (raiz + todos los descendientes).
  - `get_sections(min_level=0, max_level=10) -> list[ResourceNode]` — filtra nodos de tipo SECTION dentro del rango de niveles.

  ## Helper

  ```python
  def create_parse_result(
      root, source_path=None, source_format=None,
      parser_name=None, parse_time=None, meta=None, warnings=None
  ) -> ParseResult
  ```

  Construye un ParseResult con valores por defecto sensatos. Evita tener que instanciar `ParseResult` directamente cuando no se necesitan todos los campos.

code:
  """ParseResult — resultado completo de parsear un documento."""

  from __future__ import annotations

  from dataclasses import dataclass, field
  from datetime import datetime
  from typing import Any

  from node_type import NodeType
  from resource_node import ResourceNode


  @dataclass
  class ParseResult:
      """Resultado completo de parsear un documento.

      Contiene el arbol de nodos, metadata del proceso de parsing y
      lista de warnings. Es el tipo de retorno unificado de todos los
      parsers del registry.

      Attributes:
          root:             Nodo raiz del arbol de documento.
          source_path:      Path o URL del documento origen.
          source_format:    Formato del documento ("pdf", "markdown", "html", "docx", etc.).
          parser_name:      Nombre del parser usado ("PDFParser", "MarkdownParser", etc.).
          parser_version:   Version del parser.
          parse_time:       Duracion del parsing en segundos.
          parse_timestamp:  Momento en que se realizo el parsing.
          meta:             Metadata adicional del proceso.
          warnings:         Lista de advertencias no fatales durante el parsing.
      """

      root: ResourceNode
      source_path: str | None = None
      source_format: str | None = None
      parser_name: str | None = None
      parser_version: str | None = None
      parse_time: float | None = None
      parse_timestamp: datetime | None = None
      meta: dict[str, Any] = field(default_factory=dict)
      warnings: list[str] = field(default_factory=list)

      @property
      def success(self) -> bool:
          """True si el parsing no genero warnings."""
          return len(self.warnings) == 0

      def get_all_nodes(self) -> list[ResourceNode]:
          """Retorna todos los nodos del arbol en orden de visita (BFS).

          Returns:
              Lista plana de ResourceNode incluyendo el nodo raiz y todos
              sus descendientes.
          """
          result: list[ResourceNode] = []
          queue = [self.root]
          while queue:
              node = queue.pop(0)
              result.append(node)
              queue.extend(node.children)
          return result

      def get_sections(
          self, min_level: int = 0, max_level: int = 10
      ) -> list[ResourceNode]:
          """Retorna todos los nodos SECTION dentro del rango de niveles indicado.

          Args:
              min_level: Nivel minimo de seccion (inclusive).
              max_level: Nivel maximo de seccion (inclusive).

          Returns:
              Lista de ResourceNode de tipo SECTION dentro del rango.
          """
          return [
              node
              for node in self.get_all_nodes()
              if node.type == NodeType.SECTION
              and min_level <= node.level <= max_level
          ]


  def create_parse_result(
      root: ResourceNode,
      source_path: str | None = None,
      source_format: str | None = None,
      parser_name: str | None = None,
      parse_time: float | None = None,
      meta: dict[str, Any] | None = None,
      warnings: list[str] | None = None,
  ) -> ParseResult:
      """Helper para construir un ParseResult con valores por defecto sensatos.

      Args:
          root:          Nodo raiz del arbol de documento.
          source_path:   Path o URL del documento origen.
          source_format: Formato del documento.
          parser_name:   Nombre del parser.
          parse_time:    Duracion del parsing en segundos.
          meta:          Metadata adicional.
          warnings:      Lista de warnings.

      Returns:
          ParseResult construido con los parametros dados.
      """
      return ParseResult(
          root=root,
          source_path=source_path,
          source_format=source_format,
          parser_name=parser_name,
          parse_time=parse_time,
          meta=meta or {},
          warnings=warnings or [],
      )


id: part_py_core
name: part
lang: py
domain: core
version: 1.0.0
algebraic: sum
tags: ["message","part","union","sum_type","multipart","llm","discriminator","deserialization"]
uses_types: ["text_part_py_core","context_part_py_core","tool_part_py_core"]
file_path: python/types/core/part.py
created_at: 2026-04-05T11:21:03Z
updated_at: 2026-04-05T11:21:03Z
content_hash: 92bceeb39a522a93f5da90a34d6bce2f6b11bb201c285632989f08e691971a60
definition:
  Part = Union[TextPart, ContextPart, ToolPart]

  def part_from_dict(data: dict) -> Part:
      """Deserializa un Part desde dict, usando el campo 'type' como discriminador."""

description:
  Tipo suma de las partes posibles de un mensaje multipart: texto, referencia a contexto o llamada a herramienta. Incluye part_from_dict() para deserializacion discriminada por el campo 'type'.

notes:
  `Part` es un alias de tipo (`Union`) sobre los tres product types concretos. No es una clase — no se instancia directamente.

  `part_from_dict()` usa el campo `"type"` como discriminador: `"text"` → `TextPart`, `"context"` → `ContextPart`, `"tool"` → `ToolPart`. Lanza `ValueError` si el tipo es desconocido o falta el campo.

  Los campos del dict se pasan directamente al constructor del tipo concreto (excepto `"type"`, que ya es el valor por defecto). Esto permite round-trip: `part_from_dict(dataclasses.asdict(part))` devuelve una copia equivalente.

code:
  """Part — tipo suma de las partes posibles de un mensaje multipart."""

  from typing import Union

  from context_part import ContextPart
  from text_part import TextPart
  from tool_part import ToolPart

  Part = Union[TextPart, ContextPart, ToolPart]

  _PART_TYPES: dict[str, type] = {
      "text": TextPart,
      "context": ContextPart,
      "tool": ToolPart,
  }


  def part_from_dict(data: dict) -> Part:
      """Deserializa un Part desde un dict usando el campo 'type' como discriminador.

      Args:
          data: Dict con al menos el campo 'type' y los campos del Part correspondiente.

      Returns:
          La instancia del Part concreto (TextPart, ContextPart o ToolPart).

      Raises:
          ValueError: Si el campo 'type' no esta presente o es desconocido.
      """
      kind = data.get("type")
      if kind is None:
          raise ValueError("El dict no contiene el campo 'type' requerido para deserializar Part")
      cls = _PART_TYPES.get(kind)
      if cls is None:
          raise ValueError(f"Tipo de Part desconocido: '{kind}'. Valores validos: {list(_PART_TYPES)}")
      fields = {k: v for k, v in data.items() if k != "type"}
      return cls(**fields)


id: query_plan_py_core
name: query_plan
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["retrieval","plan","rag","intent","llm","search"]
uses_types: ["typed_query_py_core"]
file_path: python/types/core/query_plan.py
created_at: 2026-04-05T11:21:03Z
updated_at: 2026-04-05T11:21:03Z
content_hash: 733a05db518d8694e2c774d738467fa27e083429941bf0310cd51466f26fc61b
definition:
  @dataclass
  class QueryPlan:
      queries: list[TypedQuery]
      session_context: str
      reasoning: str

description:
  Plan de busqueda generado por analisis de intenciones (LLM). Contiene multiples queries tipadas y el razonamiento detras de la descomposicion.

notes:
  `session_context` resume el estado de la conversacion o sesion que el LLM analizo para generar el plan. Permite al retriever entender el contexto sin acceder al historial completo.

  `reasoning` es la cadena de pensamiento del LLM — util para debugging y observabilidad. Documenta por que se decidio descomponer la busqueda en esas queries especificas con esos tipos de contexto.

  Las queries en la lista se ordenan convencionalmente por `priority` (1=alta) para que el retriever pueda ejecutar primero las mas importantes y aplicar early stopping si ya tiene suficiente contexto.

code:
  """QueryPlan — plan de busqueda generado por analisis de intenciones (LLM)."""

  from __future__ import annotations

  from dataclasses import dataclass

  from typed_query import TypedQuery


  @dataclass
  class QueryPlan:
      """Plan de busqueda generado por analisis de intenciones.

      Contiene multiples queries tipadas y el razonamiento detras de la
      descomposicion. Generado tipicamente por un LLM que analiza el contexto
      de sesion y las intenciones del usuario.

      Attributes:
          queries:         Lista de TypedQueries a ejecutar, ordenadas por prioridad.
          session_context: Resumen del contexto de sesion que guio la descomposicion.
          reasoning:       Razonamiento del LLM sobre como descomponer la busqueda.
      """

      queries: list[TypedQuery]
      session_context: str
      reasoning: str


id: query_result_py_core
name: query_result
lang: py
domain: core
version: 1.0.0
algebraic: product
description: Resultado de una sola TypedQuery con los contextos encontrados y los directorios que se buscaron.
tags: ["retrieval","result","rag","query","search"]
uses_types: ["typed_query_py_core","matched_context_py_core"]
file_path: python/types/core/query_result.py
created_at: 2026-04-05T11:22:16Z
updated_at: 2026-04-05T11:22:16Z
content_hash: b909209661d97f5402f67e58d3a28d11199a0cca2005167d5180621730355bac
definition:
  @dataclass
  class QueryResult:
      query: TypedQuery
      matched_contexts: list[MatchedContext]
      searched_directories: list[str]

notes:
  Representa la respuesta a una unica TypedQuery dentro de un QueryPlan. El campo `searched_directories` permite auditar que directorios fueron efectivamente explorados, lo que puede diferir de los `target_directories` de la query si algunos no estaban disponibles.

  `matched_contexts` se ordena convencionalmente por score descendente, pero el tipo no lo impone — es responsabilidad del retriever.

  Un `FindResult` agrega multiples `QueryResult` para dar la vision consolidada por tipo de contexto.

code:
  """QueryResult — resultado de una sola TypedQuery con contextos encontrados."""

  from __future__ import annotations

  from dataclasses import dataclass, field

  from matched_context import MatchedContext
  from typed_query import TypedQuery


  @dataclass
  class QueryResult:
      """Resultado de una sola TypedQuery con los contextos encontrados.

      Attributes:
          query:                La TypedQuery que genero estos resultados.
          matched_contexts:     Lista de contextos encontrados, ordenados por score desc.
          searched_directories: URIs de los directorios que se exploraron durante la busqueda.
      """

      query: TypedQuery
      matched_contexts: list[MatchedContext] = field(default_factory=list)
      searched_directories: list[str] = field(default_factory=list)


id: related_context_py_core
name: related_context
lang: py
domain: core
version: 1.0.0
algebraic: product
definition:
  @dataclass
  class RelatedContext:
  uri: str
  abstract: str

tags: ["retrieval","context","relation","rag","reference"]
uses_types: []
file_path: python/types/core/related_context.py
created_at: 2026-04-05T11:20:39Z
updated_at: 2026-04-05T11:20:39Z
content_hash: 6c66c1238466e589642dad7cdebc54dfb261ff3684e31f687e439a1450c4b1b7
description:
  Referencia minima a un contexto relacionado. Contiene el URI del recurso y un abstract de su contenido. Se usa como elemento de la lista `relations` dentro de MatchedContext.

notes:
  Tipo minimalista intencionalmente. No replica el score ni el nivel de detalle del contexto padre — solo provee suficiente informacion para que el consumidor pueda decidir si explorar el recurso relacionado.

code:
  """RelatedContext — referencia a un contexto relacionado por URI y abstract."""

  from dataclasses import dataclass


  @dataclass
  class RelatedContext:
      """Referencia minima a un contexto relacionado.

      Attributes:
          uri:      Identificador unico del recurso relacionado.
          abstract: Resumen breve del contenido del recurso.
      """

      uri: str
      abstract: str


id: resource_content_type_py_core
name: resource_content_type
lang: py
domain: core
version: 1.0.0
algebraic: sum
tags: ["content-type","resource","enum","media","indexing"]
uses_types: []
file_path: python/types/core/resource_content_type.py
created_at: 2026-04-05T11:20:30Z
updated_at: 2026-04-05T11:20:30Z
content_hash: 4ac2b626e88147292b64a94ca82f1998631106651b204e7d0a0282336d3444dd
definition:
  class ResourceContentType(str, Enum):
      TEXT = "text"
      IMAGE = "image"
      VIDEO = "video"
      AUDIO = "audio"
      BINARY = "binary"

description:
  Tipo de contenido de un recurso indexable. Sum type con cinco variantes: text, image, video, audio, binary. Hereda de str para serializar directamente como string JSON.

notes:
  Hereda de `str` ademas de `Enum` para que las variantes serialicen directamente como strings (`"text"`, `"image"`, etc.) sin necesidad de `.value`. Util al almacenar en bases de datos vectoriales o JSON sin conversion extra.

  Usado por `Context` para describir el tipo de contenido del recurso que representa.

code:
  """ResourceContentType — tipo de contenido de un recurso indexable."""

  from enum import Enum


  class ResourceContentType(str, Enum):
      """Tipo de contenido de un recurso.

      Variantes:
          TEXT:   Contenido de texto plano o estructurado (markdown, code, etc.).
          IMAGE:  Imagen (PNG, JPEG, WebP, etc.).
          VIDEO:  Video (MP4, WebM, etc.).
          AUDIO:  Audio (MP3, WAV, etc.).
          BINARY: Contenido binario arbitrario (PDF, ZIP, etc.).
      """

      TEXT = "text"
      IMAGE = "image"
      VIDEO = "video"
      AUDIO = "audio"
      BINARY = "binary"


id: resource_node_py_core
name: resource_node
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["parsing","document","tree","node","hierarchy","markdown","core"]
uses_types: ["node_type_py_core"]
file_path: python/types/core/resource_node.py
created_at: 2026-04-05T11:21:19Z
updated_at: 2026-04-05T11:21:19Z
content_hash: 791dc473fb095ed010fadbde65d9c5a794ffaec2c5abb331b81a75f6293f0baa
definition:
  @dataclass
  class ResourceNode:
      type: NodeType
      title: str | None = None
      level: int = 0
      content: str = ""
      children: list["ResourceNode"] = field(default_factory=list)
      meta: dict[str, Any] = field(default_factory=dict)

description:
  Nodo en un arbol jerarquico de documento. Preserva la estructura natural del documento (secciones, subsecciones) sin perder contenido. Resultado intermedio de parsing — los parsers producen arboles de ResourceNode.

examples:
  ```python
  from node_type import NodeType
  from resource_node import ResourceNode

  root = ResourceNode(type=NodeType.ROOT, title="Mi Documento", level=0)

  section = ResourceNode(
      type=NodeType.SECTION,
      title="Introduccion",
      level=1,
      content="Este documento describe...",
  )
  root.add_child(section)

  subsection = ResourceNode(
      type=NodeType.SECTION,
      title="Contexto",
      level=2,
      content="El contexto es el siguiente...",
  )
  section.add_child(subsection)

  print(root.get_abstract())      # "Mi Documento"
  print(section.get_text())       # "Este documento describe...\n\nEl contexto es el siguiente..."
  print(section.get_text(include_children=False))  # "Este documento describe..."

  data = root.to_dict()
  restored = ResourceNode.from_dict(data)
  ```

notes:
  El campo `content` almacena el markdown del nodo directamente — no se parsea internamente. Cada nodo representa una seccion del documento delimitada por su heading y el siguiente heading del mismo nivel. Los parsers construyen el arbol llamando `add_child` en orden de aparicion en el documento. El metodo `from_dict` es el inverso exacto de `to_dict` y permite round-trip sin perdida de informacion.

documentation:
  ## Metodos

  - `add_child(child: ResourceNode) -> None` — agrega un nodo hijo a la lista de children.
  - `get_text(include_children: bool = True) -> str` — contenido concatenado del nodo y sus descendientes de forma recursiva.
  - `get_abstract(max_length: int = 256) -> str` — resumen corto: el titulo o los primeros N caracteres del contenido.
  - `get_overview(max_length: int = 4000) -> str` — overview con el contenido del nodo y lista de titulos de las secciones hijas.
  - `to_dict() -> dict` — serializacion recursiva a diccionario (JSON-compatible).
  - `from_dict(data: dict) -> ResourceNode` — deserializacion desde diccionario (classmethod).

code:
  """ResourceNode — nodo en un arbol jerarquico de documento."""

  from __future__ import annotations

  from dataclasses import dataclass, field
  from typing import Any

  from node_type import NodeType


  @dataclass
  class ResourceNode:
      """Nodo en un arbol jerarquico de documento.

      Preserva la estructura natural del documento (secciones, subsecciones)
      sin perder contenido. Cada nodo puede tener hijos (subsecciones) y
      metadata arbitraria.

      Attributes:
          type:     Tipo del nodo (ROOT o SECTION).
          title:    Titulo de la seccion, None para el nodo raiz sin titulo.
          level:    Nivel de profundidad (0=root, 1=top section, 2=subsection, etc.).
          content:  Contenido markdown de este nodo (sin incluir el de hijos).
          children: Lista de nodos hijos (subsecciones).
          meta:     Metadata arbitraria del nodo.
      """

      type: NodeType
      title: str | None = None
      level: int = 0
      content: str = ""
      children: list[ResourceNode] = field(default_factory=list)
      meta: dict[str, Any] = field(default_factory=dict)

      def add_child(self, child: ResourceNode) -> None:
          """Agrega un nodo hijo a la lista de children.

          Args:
              child: Nodo hijo a agregar.
          """
          self.children.append(child)

      def get_text(self, include_children: bool = True) -> str:
          """Retorna el contenido concatenado del nodo y opcionalmente sus hijos.

          Args:
              include_children: Si True, incluye el contenido de todos los
                                descendientes de forma recursiva.

          Returns:
              String con el contenido completo.
          """
          parts = [self.content] if self.content else []
          if include_children:
              for child in self.children:
                  child_text = child.get_text(include_children=True)
                  if child_text:
                      parts.append(child_text)
          return "\n\n".join(parts)

      def get_abstract(self, max_length: int = 256) -> str:
          """Retorna un resumen corto: el titulo o los primeros N caracteres del contenido.

          Args:
              max_length: Longitud maxima del resumen en caracteres.

          Returns:
              String con el titulo o inicio del contenido, truncado si es necesario.
          """
          if self.title:
              return self.title[:max_length]
          text = self.content.strip()
          if len(text) <= max_length:
              return text
          return text[:max_length].rstrip() + "..."

      def get_overview(self, max_length: int = 4000) -> str:
          """Retorna un overview con el contenido del nodo y lista de secciones hijas.

          Args:
              max_length: Longitud maxima del overview en caracteres.

          Returns:
              String con el contenido del nodo seguido de los titulos de sus hijos.
          """
          parts = []
          if self.title:
              parts.append(f"# {self.title}")
          if self.content:
              parts.append(self.content)
          if self.children:
              child_titles = [
                  f"- {child.title}" if child.title else f"- (section level {child.level})"
                  for child in self.children
              ]
              parts.append("## Sections\n" + "\n".join(child_titles))
          overview = "\n\n".join(parts)
          if len(overview) <= max_length:
              return overview
          return overview[:max_length].rstrip() + "..."

      def to_dict(self) -> dict:
          """Serializa el nodo y sus descendientes a un diccionario.

          Returns:
              dict con todos los campos del nodo, con children serializado
              recursivamente.
          """
          return {
              "type": self.type.value,
              "title": self.title,
              "level": self.level,
              "content": self.content,
              "children": [child.to_dict() for child in self.children],
              "meta": self.meta,
          }

      @classmethod
      def from_dict(cls, data: dict) -> ResourceNode:
          """Deserializa un dict (producido por to_dict) a un ResourceNode.

          Args:
              data: Diccionario con los campos del nodo.

          Returns:
              ResourceNode reconstruido con todos sus descendientes.
          """
          return cls(
              type=NodeType(data["type"]),
              title=data.get("title"),
              level=data.get("level", 0),
              content=data.get("content", ""),
              children=[cls.from_dict(c) for c in data.get("children", [])],
              meta=data.get("meta", {}),
          )


id: round_summary_py_core
name: round_summary
lang: py
domain: core
version: 1.0.0
algebraic: product
description: Resumen de una ronda de simulacion multi-agente con tiempos, acciones y conteos.
tags: ["simulation","round","summary","multi-agent","timeline"]
uses_types: ["agent_action_py_core"]
file_path: python/types/core/round_summary.py
created_at: 2026-04-05T11:19:14Z
updated_at: 2026-04-05T11:19:14Z
content_hash: 0592f2496b047ced5043f15441dc5aede4b674aae7194fca388bf2e81ac9f38e
definition:
  @dataclass
  class RoundSummary:
      round_num: int
      start_time: str
      end_time: str | None = None
      simulated_hour: float = 0.0
      actions: list[AgentAction] = field(default_factory=list)
      active_agents: int = 0

examples:
  ```python
  summary = RoundSummary(
      round_num=1,
      start_time="2024-01-01T12:00:00Z",
      end_time="2024-01-01T12:01:00Z",
      simulated_hour=8.0,
      actions=[action1, action2],
      active_agents=5,
  )
  print(summary.to_dict())
  ```

notes:
  Tipo producto — todos los campos obligatorios estan siempre presentes. `end_time` es `None` mientras la ronda este en progreso. `actions` contiene instancias de `AgentAction` y se serializa llamando `to_dict()` en cada elemento. Util para replay de simulaciones ronda por ronda y analisis post-simulacion.

code:
  """Resumen de una ronda de simulacion multi-agente."""

  from __future__ import annotations

  from dataclasses import dataclass, field

  from agent_action import AgentAction


  @dataclass
  class RoundSummary:
      """Resumen de una ronda de simulacion multi-agente.

      Agrupa las acciones de todos los agentes en esa ronda con tiempos y conteos.
      Permite replay de simulaciones ronda por ronda, visualizacion de timeline,
      y analisis post-simulacion.

      Attributes:
          round_num: Numero de ronda de la simulacion.
          start_time: Timestamp ISO 8601 de inicio de la ronda.
          end_time: Timestamp ISO 8601 de fin de la ronda (None si aun corre).
          simulated_hour: Hora simulada dentro de la ronda (0.0 por defecto).
          actions: Lista de acciones realizadas por los agentes en esta ronda.
          active_agents: Numero de agentes activos durante la ronda.
      """

      round_num: int
      start_time: str
      end_time: str | None = None
      simulated_hour: float = 0.0
      actions: list[AgentAction] = field(default_factory=list)
      active_agents: int = 0

      def to_dict(self) -> dict:
          """Convierte el resumen de ronda a un diccionario serializable."""
          return {
              "round_num": self.round_num,
              "start_time": self.start_time,
              "end_time": self.end_time,
              "simulated_hour": self.simulated_hour,
              "actions": [a.to_dict() for a in self.actions],
              "active_agents": self.active_agents,
          }


id: runner_status_py_core
name: runner_status
lang: py
domain: core
version: 1.0.0
algebraic: sum
description: Maquina de estados para un runner de simulacion o pipeline de larga duracion.
tags: ["simulation","runner","status","state-machine","enum"]
uses_types: []
file_path: python/types/core/runner_status.py
created_at: 2026-04-05T11:18:45Z
updated_at: 2026-04-05T11:18:45Z
content_hash: 2a7bd0e9ea9440ffdf57d5329adfb7b864cc78e3cd55ef10510c4e16d5e59ffc
definition:
  class RunnerStatus(str, Enum):
      IDLE = "idle"
      STARTING = "starting"
      RUNNING = "running"
      PAUSED = "paused"
      STOPPING = "stopping"
      STOPPED = "stopped"
      COMPLETED = "completed"
      FAILED = "failed"

notes:
  Tipo suma — exactamente uno de los estados es activo en cada momento. Hereda de `str` para que sea serializable a JSON directamente.

documentation:
  ## Transiciones validas

  ```
  IDLE -> STARTING -> RUNNING
  RUNNING -> PAUSED -> RUNNING   (resume)
  RUNNING -> STOPPING -> STOPPED
  RUNNING -> COMPLETED
  RUNNING -> FAILED
  STARTING -> FAILED
  ```

code:
  """Estado de un runner de simulacion o pipeline de larga duracion."""

  from enum import Enum


  class RunnerStatus(str, Enum):
      """Maquina de estados para un runner de simulacion o pipeline de larga duracion.

      Transiciones validas:
          IDLE -> STARTING -> RUNNING
          RUNNING -> PAUSED -> RUNNING  (resume)
          RUNNING -> STOPPING -> STOPPED
          RUNNING -> COMPLETED
          RUNNING -> FAILED
          STARTING -> FAILED
      """

      IDLE = "idle"
      STARTING = "starting"
      RUNNING = "running"
      PAUSED = "paused"
      STOPPING = "stopping"
      STOPPED = "stopped"
      COMPLETED = "completed"
      FAILED = "failed"


id: task_py_core
name: task
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["task","async","background","progress","tracking","polling"]
uses_types: ["task_status_py_core"]
file_path: python/types/core/task.py
created_at: 2026-04-05T11:18:49Z
updated_at: 2026-04-05T11:18:49Z
content_hash: df66c44f51b008ced6984a4a56b0f76f321ac8c3f67da7afd7a105ac0f6c5837
definition:
  @dataclass
  class Task:
      task_id: str
      task_type: str
      status: TaskStatus
      created_at: datetime
      updated_at: datetime
      progress: int = 0
      message: str = ""
      result: dict | None = None
      error: str | None = None
      metadata: dict = field(default_factory=dict)
      progress_detail: dict = field(default_factory=dict)

description:
  Tarea de larga duracion con seguimiento de progreso. Product type que modela el ciclo de vida completo: creacion, progreso incremental, completado exitoso o fallo. Incluye to_dict() para serializacion JSON con timestamps ISO 8601.

notes:
  El campo `progress` es un entero 0-100. El campo `progress_detail` permite informar detalles granulares (etapa actual, items procesados) sin romper la interfaz del tipo.

  El metodo `to_dict()` serializa `status` como string via `.value` y los timestamps como strings ISO 8601, haciendo el resultado directamente serializable con `json.dumps()`.

  El campo `result` almacena el output de la tarea al completar. El campo `error` almacena la descripcion del fallo. Solo uno de los dos deberia estar poblado en un estado terminal.

code:
  """Task — tarea de larga duracion con seguimiento de progreso."""

  from dataclasses import dataclass, field
  from datetime import datetime, timezone

  from task_status import TaskStatus


  @dataclass
  class Task:
      """Tarea de larga duracion con seguimiento de progreso.

      Attributes:
          task_id:         UUID que identifica la tarea de forma unica.
          task_type:       Tipo libre: "graph_build", "simulation", "report_generation", etc.
          status:          Estado actual del ciclo de vida (TaskStatus).
          created_at:      Timestamp de creacion (UTC).
          updated_at:      Timestamp de ultima actualizacion (UTC).
          progress:        Porcentaje de avance 0-100.
          message:         Mensaje de estado legible por el usuario.
          result:          Resultado serializado al completar, None si no ha completado.
          error:           Descripcion del error al fallar, None si no ha fallado.
          metadata:        Datos arbitrarios asociados a la tarea (parametros de entrada, etc.).
          progress_detail: Detalles granulares de progreso (etapa actual, items procesados, etc.).
      """

      task_id: str
      task_type: str
      status: TaskStatus
      created_at: datetime
      updated_at: datetime
      progress: int = 0
      message: str = ""
      result: dict | None = None
      error: str | None = None
      metadata: dict = field(default_factory=dict)
      progress_detail: dict = field(default_factory=dict)

      def to_dict(self) -> dict:
          """Serializa la tarea a dict con timestamps en formato ISO 8601.

          Returns:
              dict con todos los campos de la tarea listos para JSON.
          """
          return {
              "task_id": self.task_id,
              "task_type": self.task_type,
              "status": self.status.value,
              "created_at": self.created_at.isoformat(),
              "updated_at": self.updated_at.isoformat(),
              "progress": self.progress,
              "message": self.message,
              "result": self.result,
              "error": self.error,
              "metadata": self.metadata,
              "progress_detail": self.progress_detail,
          }


id: task_status_py_core
name: task_status
lang: py
domain: core
version: 1.0.0
algebraic: sum
tags: ["task","status","enum","async","background"]
uses_types: []
file_path: python/types/core/task_status.py
created_at: 2026-04-05T11:18:45Z
updated_at: 2026-04-05T11:18:45Z
content_hash: 54df263cd377decbaf2ce32456315548e85ccd359add15a4ce36b857af7c0664
definition:
  class TaskStatus(str, Enum):
      PENDING = "pending"
      PROCESSING = "processing"
      COMPLETED = "completed"
      FAILED = "failed"

description:
  Estado de una tarea de larga duracion (graph building, simulation, report generation). Enum sum type con cuatro variantes: pending, processing, completed, failed.

notes:
  Hereda de `str` para que los valores sean directamente serializables a JSON sin conversion adicional.
  Usado por `Task` para expresar el ciclo de vida de una tarea background.

code:
  """TaskStatus — estado de una tarea de larga duracion."""

  from enum import Enum


  class TaskStatus(str, Enum):
      """Estado de una tarea de larga duracion.

      Valores posibles:
          PENDING    — creada, en cola.
          PROCESSING — en ejecucion activa.
          COMPLETED  — terminada con exito.
          FAILED     — terminada con error.
      """

      PENDING = "pending"
      PROCESSING = "processing"
      COMPLETED = "completed"
      FAILED = "failed"


id: text_part_py_core
name: text_part
lang: py
domain: core
version: 1.0.0
algebraic: product
definition:
  @dataclass
  class TextPart:
  text: str = ""
  type: str = "text"

tags: ["message","part","text","multipart","llm","conversation"]
uses_types: []
file_path: python/types/core/text_part.py
created_at: 2026-04-05T11:20:17Z
updated_at: 2026-04-05T11:20:17Z
content_hash: 991651a24a877b6fa46c110c0de9c99cbcc0fb8edda72cf485a85a41f444bd90
description:
  Parte de texto plano en un mensaje multipart. Contiene el contenido textual y el discriminador literal 'text' para deserializacion por tipo.

notes:
  El campo `type` actua como discriminador literal en el tipo suma `Part`. Siempre vale `"text"`.

  Es el tipo de parte mas basico: un contenedor simple para texto plano dentro de un `Message`.

code:
  """TextPart — parte de texto de un mensaje multipart."""

  from dataclasses import dataclass, field


  @dataclass
  class TextPart:
      """Parte de texto plano en un mensaje multipart.

      Attributes:
          text: Contenido textual de la parte.
          type: Discriminador literal, siempre "text".
      """

      text: str = ""
      type: str = field(default="text", init=True)


id: tool_part_py_core
name: tool_part
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["message","part","tool","tool_call","function_call","multipart","llm","tracing","tokens"]
uses_types: []
file_path: python/types/core/tool_part.py
created_at: 2026-04-05T11:20:41Z
updated_at: 2026-04-05T11:20:41Z
content_hash: 7a30e5aee84e87eb1c92b8d47884b670192e02ec4d8aa95f77175f2d1a2f3907
definition:
  @dataclass
  class ToolPart:
      tool_id: str = ""
      tool_name: str = ""
      tool_input: dict | None = None
      tool_output: str = ""
      tool_status: str = "pending"
      duration_ms: float | None = None
      prompt_tokens: int | None = None
      completion_tokens: int | None = None
      type: str = "tool"

description:
  Registro de una llamada a herramienta (tool call) dentro de un mensaje multipart. Captura input, output, estado del ciclo de vida y metricas de uso de tokens para trazabilidad y presupuesto.

notes:
  `tool_status` modela el ciclo de vida de la llamada: "pending" (encolada), "running" (en ejecucion), "completed" (finalizada con exito), "error" (fallida).

  `tool_input` es un dict arbitrario con los argumentos. `tool_output` es siempre string para mantener compatibilidad con la API de herramientas de LLMs (OpenAI, Anthropic).

  `duration_ms`, `prompt_tokens` y `completion_tokens` son opcionales — se poblan cuando la herramienta instrumenta su ejecucion. Utiles para presupuesto de tokens y profiling.

  El campo `type` actua como discriminador literal en el tipo suma `Part`. Siempre vale `"tool"`.

code:
  """ToolPart — registro de una llamada a herramienta en un mensaje multipart."""

  from dataclasses import dataclass, field


  @dataclass
  class ToolPart:
      """Registro de una llamada a herramienta (tool call) dentro de un mensaje multipart.

      Attributes:
          tool_id:           Identificador unico de la invocacion de la herramienta.
          tool_name:         Nombre de la herramienta invocada.
          tool_input:        Argumentos de entrada pasados a la herramienta.
          tool_output:       Resultado devuelto por la herramienta como string.
          tool_status:       Estado de la llamada: "pending" | "running" | "completed" | "error".
          duration_ms:       Duracion de la llamada en milisegundos.
          prompt_tokens:     Tokens de entrada consumidos por la herramienta (si aplica).
          completion_tokens: Tokens de salida generados por la herramienta (si aplica).
          type:              Discriminador literal, siempre "tool".
      """

      tool_id: str = ""
      tool_name: str = ""
      tool_input: dict | None = None
      tool_output: str = ""
      tool_status: str = "pending"
      duration_ms: float | None = None
      prompt_tokens: int | None = None
      completion_tokens: int | None = None
      type: str = field(default="tool", init=True)


id: typed_query_py_core
name: typed_query
lang: py
domain: core
version: 1.0.0
algebraic: product
tags: ["retrieval","query","rag","intent","search","plan"]
uses_types: ["context_type_py_core"]
file_path: python/types/core/typed_query.py
created_at: 2026-04-05T11:21:03Z
updated_at: 2026-04-05T11:21:03Z
content_hash: 5ef9fba530c4f0be0725c75c3fe048b07e57f49f4911feb37afaa5ada783ecf9
definition:
  @dataclass
  class TypedQuery:
      query: str
      context_type: ContextType | None
      intent: str
      priority: int = 3
      target_directories: list[str] = field(default_factory=list)

description:
  Query tipada que resulta del analisis de intenciones. Un plan de busqueda se descompone en multiples TypedQueries, cada una apuntando a un tipo de contexto diferente.

notes:
  `priority` sigue la convencion 1=alta, 5=baja (similar a niveles de issue). Valor por defecto 3 es prioridad media.

  `context_type = None` indica que la query aplica a todos los tipos de contexto sin filtro. Util para queries generales que el retriever distribuye entre todas las categorias.

  `target_directories` vacio significa buscar en todos los directorios disponibles. Con URIs especificos, el retriever limita la busqueda al subconjunto indicado — util para reducir ruido en colecciones grandes.

code:
  """TypedQuery — query tipada resultante del analisis de intenciones."""

  from __future__ import annotations

  from dataclasses import dataclass, field

  from context_type import ContextType


  @dataclass
  class TypedQuery:
      """Query tipada que resulta del analisis de intenciones.

      Un plan de busqueda se descompone en multiples TypedQueries, cada una
      apuntando a un tipo de contexto diferente. Permite estrategias de
      retrieval especificas por categoria.

      Attributes:
          query:              Texto de la query de busqueda.
          context_type:       Tipo de contexto objetivo (MEMORY, RESOURCE, SKILL) o None para todos.
          intent:             Descripcion de la intencion detras de esta query.
          priority:           Prioridad 1-5 (1 es mayor prioridad).
          target_directories: URIs de directorios donde buscar. Lista vacia = buscar en todos.
      """

      query: str
      context_type: ContextType | None
      intent: str
      priority: int = 3
      target_directories: list[str] = field(default_factory=list)


id: deduplication_result_py_datascience
name: deduplication_result
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["deduplication","entity","merge","knowledge-graph","nlp","datascience"]
uses_types: ["entity_candidate_py_datascience"]
file_path: python/types/datascience/deduplication_result.py
created_at: 2026-04-05T11:22:32Z
updated_at: 2026-04-05T11:22:32Z
content_hash: 7d4de365b887aa83eba1882ffb31655f9258b0ab610370a03bc0ab0ce07b45e5
definition:
  @dataclass
  class DeduplicationResult:
      entities: list[EntityCandidate]
      entity_id_map: dict[str, str]
      name_to_id: dict[str, str]
      merge_log: list[dict] = field(default_factory=list)
      total_before: int = 0
      total_after: int = 0

description:
  Resultado de deduplicacion de entidades. name_to_id mapea todos los nombres originales (incluyendo aliases mergeados) a su ID final para resolver relaciones.

examples:
  ```python
  from python.types.datascience.deduplication_result import DeduplicationResult
  from python.types.datascience.entity_candidate import EntityCandidate

  result = DeduplicationResult(
      entities=[
          EntityCandidate(
              name="John Smith",
              name_normalized="john smith",
              merged_from=["John Smith", "Smith, John"],
          ),
      ],
      entity_id_map={"john smith": "ent_001"},
      name_to_id={
          "John Smith": "ent_001",
          "Smith, John": "ent_001",
          "john smith": "ent_001",
      },
      merge_log=[
          {"merged": ["John Smith", "Smith, John"], "into": "John Smith", "score": 0.92}
      ],
      total_before=2,
      total_after=1,
  )
  ```

notes:
  El `name_to_id` es la clave para la resolucion de relaciones. Cuando una relacion dice `from_name="Smith, John"`, se puede buscar en `name_to_id` y encontrar el ID `ent_001` aunque el nombre canonico sea `"John Smith"`. `entity_id_map` es un subset que usa solo nombres normalizados, util para lookup durante la misma fase de deduplicacion.

documentation:
  ## Campos

  - `entities`: lista final de `EntityCandidate` despues de deduplicacion
  - `entity_id_map`: mapa `nombre_normalizado → entity_id` para lookup rapido
  - `name_to_id`: mapa de TODOS los nombres originales (incluyendo aliases) a su ID final
  - `merge_log`: lista de eventos de merge, cada uno con `{"merged": [...], "into": "...", "score": 0.92}`
  - `total_before`: numero de entidades antes de deduplicacion
  - `total_after`: numero de entidades despues de deduplicacion

code:
  """DeduplicationResult — resultado del proceso de deduplicacion de entidades."""

  from dataclasses import dataclass, field

  from python.types.datascience.entity_candidate import EntityCandidate


  @dataclass
  class DeduplicationResult:
      """Resultado de deduplicacion de entidades.

      El `name_to_id` mapea TODOS los nombres originales (incluyendo los
      mergeados) a su ID final, permitiendo resolver relaciones que usan
      cualquier variante del nombre.
      """

      entities: list[EntityCandidate]
      entity_id_map: dict[str, str]
      name_to_id: dict[str, str]
      merge_log: list[dict] = field(default_factory=list)
      total_before: int = 0
      total_after: int = 0


id: entity_candidate_py_datascience
name: entity_candidate
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["extraction","entity","llm","knowledge-graph","nlp","datascience"]
uses_types: []
file_path: python/types/datascience/entity_candidate.py
created_at: 2026-04-05T11:22:16Z
updated_at: 2026-04-05T11:22:16Z
content_hash: 3b4a628f28e455edb147fef5e427e2a45cda45ddf1c7fdf9d342525bcd457c0e
definition:
  @dataclass
  class EntityCandidate:
      name: str
      name_normalized: str = ""
      type_ref: str = ""
      type_label: str = ""
      attributes: dict = field(default_factory=dict)
      confidence: float = 0.0
      source_chunk_indices: list[int] = field(default_factory=list)
      merged_from: list[str] = field(default_factory=list)

description:
  Candidato de entidad extraido por el LLM. Puede venir de un solo chunk o ser el resultado de mergear multiples extracciones. merged_from rastrea los nombres originales para debugging.

examples:
  ```python
  from python.types.datascience.entity_candidate import EntityCandidate

  candidate = EntityCandidate(
      name="John Smith",
      name_normalized="john smith",
      type_ref="osint_person_go_cybersecurity",
      type_label="Person",
      attributes={"role": "CEO", "company": "Acme Corp"},
      confidence=0.92,
      source_chunk_indices=[0, 3],
      merged_from=["John Smith", "Smith, John"],
  )

  print(candidate.to_dict())
  ```

notes:
  Tipo inmutable en su intencion pero declarado como dataclass mutable para permitir que las fases de deduplicacion rellenen `name_normalized` y que el pipeline complete los campos opcionales progresivamente. `merged_from` es la fuente de verdad para auditar que entidades se colapsaron juntas.

documentation:
  ## Campos

  - `name`: nombre crudo extraido del texto
  - `name_normalized`: nombre normalizado para deduplicacion (se llena en la fase de dedup)
  - `type_ref`: registry type ID del tipo de entidad (ej: `osint_person_go_cybersecurity`)
  - `type_label`: label humano del tipo (ej: `"Person"`)
  - `attributes`: metadata extraida del texto como diccionario libre
  - `confidence`: score de confianza 0.0-1.0
  - `source_chunk_indices`: indices de los chunks donde aparecio la entidad
  - `merged_from`: nombres originales si el candidato fue creado por merge de varios

code:
  """EntityCandidate — candidato de entidad extraido por el LLM."""

  from dataclasses import dataclass, field


  @dataclass
  class EntityCandidate:
      """Candidato de entidad extraido por el LLM.

      Puede venir de un solo chunk o ser el resultado de mergear multiples
      extracciones. `merged_from` rastrea los nombres originales para debugging.
      """

      name: str
      name_normalized: str = ""
      type_ref: str = ""
      type_label: str = ""
      attributes: dict = field(default_factory=dict)
      confidence: float = 0.0
      source_chunk_indices: list[int] = field(default_factory=list)
      merged_from: list[str] = field(default_factory=list)

      def to_dict(self) -> dict:
          """Serializa el candidato a un diccionario."""
          return {
              "name": self.name,
              "name_normalized": self.name_normalized,
              "type_ref": self.type_ref,
              "type_label": self.type_label,
              "attributes": self.attributes,
              "confidence": self.confidence,
              "source_chunk_indices": self.source_chunk_indices,
              "merged_from": self.merged_from,
          }


id: extraction_result_py_datascience
name: extraction_result
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["extraction","result","pipeline","knowledge-graph","datascience"]
file_path: python/types/datascience/extraction_result.py
created_at: 2026-04-05T11:22:24Z
updated_at: 2026-04-05T11:22:24Z
content_hash: 648dba0953c99ddea4782c5b004b6ba087fb6972dd6e1a200b7576a6d897b7b6
definition:
  @dataclass
  class ExtractionResult:
      entities: list[EntityCandidate]
      relations: list[RelationCandidate]
      stats: ExtractionStats = field(default_factory=ExtractionStats)

description:
  Resultado final del pipeline de extraccion. Contiene las listas deduplicadas de entidades y relaciones junto con las estadisticas del proceso completo.

uses_types:
  ["entity_candidate_py_datascience","relation_candidate_py_datascience","extraction_stats_py_datascience"]

examples:
  ```python
  from python.types.datascience.entity_candidate import EntityCandidate
  from python.types.datascience.extraction_result import ExtractionResult
  from python.types.datascience.extraction_stats import ExtractionStats
  from python.types.datascience.relation_candidate import RelationCandidate

  result = ExtractionResult(
      entities=[
          EntityCandidate(name="John Smith", type_label="Person", confidence=0.9),
      ],
      relations=[
          RelationCandidate(
              from_name="John Smith",
              to_name="Acme Corp",
              from_id="ent_001",
              to_id="ent_002",
              relation_type="works_at",
          ),
      ],
      stats=ExtractionStats(total_chunks=10, final_entities_count=1, final_relations_count=1),
  )
  ```

notes:
  Tipo de salida del pipeline completo de extraccion. Agrupa todo lo necesario para persistir el resultado en una base de datos de grafos o para pasarlo a la siguiente fase del pipeline (enriquecimiento, visualizacion, etc.).

documentation:
  ## Campos

  - `entities`: lista final de `EntityCandidate` deduplicados
  - `relations`: lista final de `RelationCandidate` deduplicadas con IDs resueltos
  - `stats`: `ExtractionStats` con metricas del proceso completo

code:
  """ExtractionResult — resultado final del pipeline de extraccion."""

  from dataclasses import dataclass, field

  from python.types.datascience.entity_candidate import EntityCandidate
  from python.types.datascience.extraction_stats import ExtractionStats
  from python.types.datascience.relation_candidate import RelationCandidate


  @dataclass
  class ExtractionResult:
      """Resultado final del pipeline de extraccion de entidades y relaciones.

      Contiene las listas deduplicadas de entidades y relaciones junto con
      las estadisticas del proceso completo.
      """

      entities: list[EntityCandidate]
      relations: list[RelationCandidate]
      stats: ExtractionStats = field(default_factory=ExtractionStats)


id: extraction_stats_py_datascience
name: extraction_stats
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["extraction","stats","reporting","debugging","datascience"]
uses_types: []
file_path: python/types/datascience/extraction_stats.py
created_at: 2026-04-05T11:22:16Z
updated_at: 2026-04-05T11:22:16Z
content_hash: 653c9fd93a92be02e97bd1cbfd55b12487e03725753528c876faa6bd071df0a6
definition:
  @dataclass
  class ExtractionStats:
      total_chunks: int = 0
      total_chars: int = 0
      raw_entities_count: int = 0
      final_entities_count: int = 0
      entities_merged: int = 0
      raw_relations_count: int = 0
      final_relations_count: int = 0
      relations_merged: int = 0
      relations_discarded: int = 0
      entity_types_found: dict[str, int] = field(default_factory=dict)
      relation_types_found: dict[str, int] = field(default_factory=dict)
      processing_time_seconds: float = 0.0

description:
  Estadisticas del proceso de extraccion de entidades y relaciones. Registra conteos antes y despues de deduplicacion, tiempo de procesamiento y distribucion de tipos.

examples:
  ```python
  from python.types.datascience.extraction_stats import ExtractionStats

  stats = ExtractionStats(
      total_chunks=42,
      total_chars=85000,
      raw_entities_count=120,
      final_entities_count=34,
      entities_merged=86,
      raw_relations_count=200,
      final_relations_count=67,
      relations_discarded=5,
      entity_types_found={"Person": 18, "Organization": 16},
      processing_time_seconds=12.4,
  )
  ```

notes:
  Acumulable progresivamente durante el pipeline. Util para detectar documentos con baja densidad de informacion (pocos entities por chunk) o pipelines lentos. El ratio `entities_merged / raw_entities_count` indica que tan redundante fue el texto fuente.

documentation:
  ## Campos

  - `total_chunks`: numero total de chunks procesados
  - `total_chars`: caracteres totales procesados
  - `raw_entities_count`: entidades extraidas antes de deduplicacion
  - `final_entities_count`: entidades restantes despues de deduplicacion
  - `entities_merged`: cuantas entidades se colapsaron en otras
  - `raw_relations_count`: relaciones extraidas antes de deduplicacion
  - `final_relations_count`: relaciones restantes despues de deduplicacion
  - `relations_merged`: relaciones duplicadas que se colapsaron
  - `relations_discarded`: self-loops o relaciones sin match de entidad descartadas
  - `entity_types_found`: mapa `type_ref → count` de entidades por tipo
  - `relation_types_found`: mapa `relation_type → count` de relaciones por tipo
  - `processing_time_seconds`: tiempo total del pipeline en segundos

code:
  """ExtractionStats — estadisticas del proceso de extraccion."""

  from dataclasses import dataclass, field


  @dataclass
  class ExtractionStats:
      """Estadisticas del proceso de extraccion.

      Util para reporting y debugging. Registra conteos antes y despues de
      deduplicacion, tiempo de procesamiento y distribucion de tipos encontrados.
      """

      total_chunks: int = 0
      total_chars: int = 0
      raw_entities_count: int = 0
      final_entities_count: int = 0
      entities_merged: int = 0
      raw_relations_count: int = 0
      final_relations_count: int = 0
      relations_merged: int = 0
      relations_discarded: int = 0
      entity_types_found: dict[str, int] = field(default_factory=dict)
      relation_types_found: dict[str, int] = field(default_factory=dict)
      processing_time_seconds: float = 0.0


id: relation_candidate_py_datascience
name: relation_candidate
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["extraction","relation","llm","knowledge-graph","nlp","datascience"]
uses_types: []
file_path: python/types/datascience/relation_candidate.py
created_at: 2026-04-05T11:22:16Z
updated_at: 2026-04-05T11:22:16Z
content_hash: 16ae73b1752306e04dae0fea236f4897098fcd347e30be51bdf795430996008f
definition:
  @dataclass
  class RelationCandidate:
      from_name: str
      to_name: str
      from_id: str = ""
      to_id: str = ""
      relation_type: str = ""
      description: str = ""
      confidence: float = 0.0
      source_chunk_index: int = -1

description:
  Candidato de relacion entre dos entidades extraido por el LLM. from_id y to_id se resuelven durante la deduplicacion.

examples:
  ```python
  from python.types.datascience.relation_candidate import RelationCandidate

  rel = RelationCandidate(
      from_name="John Smith",
      to_name="Acme Corp",
      relation_type="works_at",
      description="John Smith es CEO de Acme Corp",
      confidence=0.88,
      source_chunk_index=2,
  )

  print(rel.to_dict())
  ```

notes:
  Los campos `from_id` y `to_id` empiezan vacios y se resuelven contra el `name_to_id` del `DeduplicationResult`. Relaciones donde no se puede resolver alguno de los IDs se descartan y se cuentan en `ExtractionStats.relations_discarded`. Los self-loops (`from_id == to_id`) tambien se descartan.

documentation:
  ## Campos

  - `from_name`: nombre crudo de la entidad origen tal como aparece en el texto
  - `to_name`: nombre crudo de la entidad destino
  - `from_id`: entity ID resuelto (se llena en la fase de deduplicacion)
  - `to_id`: entity ID resuelto del destino
  - `relation_type`: tipo de relacion segun los presets del dominio
  - `description`: descripcion textual libre de la relacion
  - `confidence`: score de confianza 0.0-1.0
  - `source_chunk_index`: indice del chunk del que se extrajo (-1 si es desconocido)

code:
  """RelationCandidate — candidato de relacion extraido por el LLM."""

  from dataclasses import dataclass


  @dataclass
  class RelationCandidate:
      """Candidato de relacion entre dos entidades extraido por el LLM.

      `from_name` y `to_name` contienen los nombres crudos del texto. `from_id`
      y `to_id` se llenan durante la fase de deduplicacion cuando se resuelven
      contra los EntityCandidate finales.
      """

      from_name: str
      to_name: str
      from_id: str = ""
      to_id: str = ""
      relation_type: str = ""
      description: str = ""
      confidence: float = 0.0
      source_chunk_index: int = -1

      def to_dict(self) -> dict:
          """Serializa el candidato a un diccionario."""
          return {
              "from_name": self.from_name,
              "to_name": self.to_name,
              "from_id": self.from_id,
              "to_id": self.to_id,
              "relation_type": self.relation_type,
              "description": self.description,
              "confidence": self.confidence,
              "source_chunk_index": self.source_chunk_index,
          }


id: score_distribution_py_datascience
name: score_distribution
lang: py
domain: datascience
version: 1.0.0
algebraic: product
tags: ["retrieval","score","distribution","statistics","debugging","rag"]
uses_types: []
file_path: python/types/datascience/score_distribution.py
created_at: 2026-04-05T11:21:35Z
updated_at: 2026-04-05T11:21:35Z
content_hash: f3ef867ae93759d697915548bad451d12fb969bde50e461cea0eb959f6efebf1
definition:
  @dataclass
  class ScoreDistribution:
      scores: list[tuple[str, float]]
      min_score: float = 0.0
      max_score: float = 0.0
      mean_score: float = 0.0
      threshold: float = 0.0

description:
  Estadisticas de distribucion de scores para visualizacion y debugging de retrieval. Incluye classmethod from_scores() que calcula stats automaticamente y to_dict() con count y above_threshold.

notes:
  `from_scores(uri_scores, threshold)` es el constructor principal. Ordena los scores de mayor a menor y calcula min, max y mean automaticamente. El parametro `threshold` solo se almacena en el campo del mismo nombre — el filtrado real lo aplica el retriever.

  `to_dict()` incluye `count` (total de items) y `above_threshold` (items con score >= threshold). Estos campos adicionales facilitan el logging y la observabilidad sin necesidad de recalcular en el consumidor.

  Los scores estan en rango 0.0-1.0 tipicamente, aunque el tipo no lo valida — la responsabilidad de normalizacion es del retriever.

code:
  """ScoreDistribution — estadisticas de distribucion de scores para retrieval."""

  from __future__ import annotations

  from dataclasses import dataclass, field


  @dataclass
  class ScoreDistribution:
      """Estadisticas de distribucion de scores para visualizacion y debugging de retrieval.

      Attributes:
          scores:     Lista de tuplas (uri, score) ordenadas de mayor a menor score.
          min_score:  Score minimo en la distribucion.
          max_score:  Score maximo en la distribucion.
          mean_score: Score medio de la distribucion.
          threshold:  Umbral aplicado para filtrar resultados.
      """

      scores: list[tuple[str, float]] = field(default_factory=list)
      min_score: float = 0.0
      max_score: float = 0.0
      mean_score: float = 0.0
      threshold: float = 0.0

      @classmethod
      def from_scores(
          cls,
          uri_scores: list[tuple[str, float]],
          threshold: float = 0.0,
      ) -> ScoreDistribution:
          """Construye ScoreDistribution calculando estadisticas desde una lista de scores.

          Args:
              uri_scores: Lista de tuplas (uri, score), no necesariamente ordenada.
              threshold:  Umbral de inclusion. Solo afecta el campo `threshold` del resultado.

          Returns:
              ScoreDistribution con scores ordenados desc y estadisticas calculadas.
          """
          if not uri_scores:
              return cls(scores=[], threshold=threshold)

          sorted_scores = sorted(uri_scores, key=lambda x: x[1], reverse=True)
          raw_scores = [s for _, s in sorted_scores]

          return cls(
              scores=sorted_scores,
              min_score=min(raw_scores),
              max_score=max(raw_scores),
              mean_score=sum(raw_scores) / len(raw_scores),
              threshold=threshold,
          )

      def to_dict(self) -> dict:
          """Serializa la distribucion a dict incluyendo count y above_threshold.

          Returns:
              dict con scores, stats, count total y count de scores sobre el umbral.
          """
          above = [s for _, s in self.scores if s >= self.threshold]
          return {
              "scores": [{"uri": uri, "score": score} for uri, score in self.scores],
              "min_score": self.min_score,
              "max_score": self.max_score,
              "mean_score": self.mean_score,
              "threshold": self.threshold,
              "count": len(self.scores),
              "above_threshold": len(above),
          }


id: BQClient_py_infra
name: BQClient
lang: py
domain: infra
version: 1.0.0
algebraic: product
definition:
  @dataclass
  class BQClient:
  project_id: str
  _client: bigquery.Client

tags: ["bigquery","gcp","client","google-cloud"]
uses_types: []
file_path: python/types/infra/bq_client.py
created_at: 2026-04-07T09:56:37Z
updated_at: 2026-04-07T09:56:37Z
content_hash: 73b45291163bf58bfe764258322ce6d2cb8e30928e4be59347fb7c8dc013a34e
description:
  Cliente para Google BigQuery. Contiene el project_id y el cliente oficial del SDK. Se obtiene con bq_auth().

examples:
  ```python
  from bigquery import bq_auth, BQClient

  client: BQClient = bq_auth("my-project")
  print(client.project_id)  # "my-project"
  client.close()
  ```

notes:
  Wrapper sobre `google.cloud.bigquery.Client`. El campo `_client` es interno y no serializable.
  Se usa como context manager: `with bq_auth() as client: ...`

code:
  """Tipo BQClient para Google BigQuery."""

  from dataclasses import dataclass, field

  # Nota: la implementacion real esta en python/functions/bigquery/client.py
  # Este archivo existe solo como referencia del tipo para el registry.

  @dataclass
  class BQClient:
      """Cliente para Google BigQuery.

      Attributes:
          project_id: ID del proyecto GCP.
          _client: Cliente oficial de BigQuery (interno, no serializable).
      """
      project_id: str
      _client: object = field(repr=False, default=None)


id: classified_file_py_infra
name: classified_file
lang: py
domain: infra
version: 1.0.0
algebraic: product
definition:
  @dataclass
  class ClassifiedFile:
  path: str
  rel_path: str
  classification: str

tags: ["file","directory","scan","classification","infra"]
uses_types: []
file_path: python/types/infra/classified_file.py
created_at: 2026-04-05T11:19:41Z
updated_at: 2026-04-05T11:19:41Z
content_hash: 8d988b0df4dee410165203fe2f5783825d504adbd41a9c1005d52d7fdf0e55da
description:
  Archivo clasificado durante un escaneo de directorio. Contiene el path absoluto, el path relativo a la raiz del escaneo (con forward slashes), y la clasificacion: 'processable' o 'unsupported'.

examples:
  ```python
  from classified_file import ClassifiedFile

  f = ClassifiedFile(
      path="/home/user/docs/report.pdf",
      rel_path="docs/report.pdf",
      classification="processable",
  )
  ```

notes:
  Tipo producto inmutable usado como elemento de `DirectoryScanResult.processable` y `DirectoryScanResult.unsupported`. El campo `rel_path` siempre usa forward slashes independientemente del OS para garantizar consistencia cross-platform.

documentation:
  ## Campos

  - `path` — path absoluto del archivo en el sistema de archivos.
  - `rel_path` — path relativo a la raiz del escaneo, siempre con forward slashes (`/`).
  - `classification` — `"processable"` si la extension esta entre las soportadas, `"unsupported"` en caso contrario.

code:
  """ClassifiedFile — archivo clasificado durante un escaneo de directorio."""

  from dataclasses import dataclass


  @dataclass
  class ClassifiedFile:
      """Archivo clasificado durante un escaneo de directorio.

      Contiene el path absoluto, el path relativo a la raiz del escaneo,
      y la clasificacion del archivo segun su extension.
      """

      path: str
      rel_path: str
      classification: str  # "processable" | "unsupported"


id: directory_scan_result_py_infra
name: directory_scan_result
lang: py
domain: infra
version: 1.0.0
algebraic: product
tags: ["directory","scan","filesystem","result","infra"]
uses_types: ["classified_file_py_infra"]
file_path: python/types/infra/directory_scan_result.py
created_at: 2026-04-05T11:20:13Z
updated_at: 2026-04-05T11:20:18Z
content_hash: e66fec59793d2c103a8384d05c8e4b1a6ad22c27f07d583830b09e9b1427dea6
definition:
  @dataclass
  class DirectoryScanResult:
      root: str
      processable: list[ClassifiedFile] = field(default_factory=list)
      unsupported: list[ClassifiedFile] = field(default_factory=list)
      skipped: list[str] = field(default_factory=list)
      warnings: list[str] = field(default_factory=list)

description:
  Resultado de un escaneo de arbol de directorios. Agrupa archivos procesables, no soportados, paths saltados (con razon) y warnings generados durante el escaneo.

examples:
  ```python
  from scan_directory import scan_directory

  result = scan_directory("/data/docs", supported_extensions={".pdf", ".md"})
  print(f"Procesables: {len(result.processable)}")
  print(f"No soportados: {len(result.unsupported)}")
  for s in result.skipped:
      print(f"  Saltado: {s}")
  ```

notes:
  Producido exclusivamente por `scan_directory`. Los campos `processable` y `unsupported` estan ordenados por `rel_path`. El campo `skipped` incluye tanto directorios podados como archivos individuales ignorados (dot files, symlinks, archivos vacios).

documentation:
  ## Campos

  - `root` — path absoluto del directorio raiz escaneado.
  - `processable` — lista de `ClassifiedFile` con clasificacion `"processable"`.
  - `unsupported` — lista de `ClassifiedFile` con clasificacion `"unsupported"`.
  - `skipped` — lista de strings con formato `"path (reason)"` para archivos/dirs omitidos.
  - `warnings` — mensajes de advertencia no fatales generados durante el escaneo.

code:
  """DirectoryScanResult — resultado de un escaneo de arbol de directorios."""

  from dataclasses import dataclass, field

  from classified_file import ClassifiedFile


  @dataclass
  class DirectoryScanResult:
      """Resultado de un escaneo de arbol de directorios.

      Agrupa los archivos procesables, no soportados, los paths saltados
      y los warnings generados durante el escaneo.
      """

      root: str
      processable: list[ClassifiedFile] = field(default_factory=list)
      unsupported: list[ClassifiedFile] = field(default_factory=list)
      skipped: list[str] = field(default_factory=list)   # "path (reason)"
      warnings: list[str] = field(default_factory=list)

      def all_processable_files(self) -> list[ClassifiedFile]:
          """Alias de processable para una API mas clara."""
          return self.processable


id: pdf_doc_py_infra
name: pdf_doc
lang: py
domain: infra
version: 1.0.0
algebraic: product
tags: ["pdf","document","builder","fpdf2","infra"]
uses_types: []
file_path: python/types/infra/pdf_doc.py
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 6bc2a49ad60d4315563e1552be255369eca3d04b3788d385b4af7b4eaf3b0030
definition:
  @dataclass
  class PDFDoc:
      fpdf: Any = None
      pages: list = field(default_factory=list)
      title: str = ""
      author: str = ""
      subject: str = ""
      default_font: str = "Helvetica"
      margin_left: float = 20.0
      margin_right: float = 20.0
      margin_top: float = 20.0
      margin_bottom: float = 20.0
      header_text: str = ""
      footer_text: str = ""
      page_numbers: bool = False

description:
  Wrapper de documento PDF generado con fpdf2. Contiene el objeto FPDF interno con el estado mutable del documento y la configuracion de metadatos, margenes, header y footer.

examples:
  ```python
  from pdf_doc import PDFDoc

  # El doc se crea via pdf_create, no directamente
  # doc = pdf_create(title="Mi Reporte", author="Agente")
  # doc.title  =>  "Mi Reporte"
  # doc.fpdf   =>  <fpdf2.FPDF object>
  ```

notes:
  Este tipo es el carrier del estado del builder. Todas las funciones `pdf_add_*` reciben un `PDFDoc` y retornan uno modificado (API fluent). El campo `fpdf` contiene el estado mutable — no es thread-safe. Creado exclusivamente via `pdf_create_py_infra`.

documentation:
  ## Campos

  - `fpdf` — objeto `fpdf2.FPDF` con el estado interno del documento. Se inicializa en `pdf_create`. No serializable directamente — usar `pdf_save` para obtener bytes o archivo.
  - `pages` — lista de dicts con metadata de cada pagina añadida: `{orientation, width, height}`.
  - `title` — titulo del documento (metadata PDF, visible en propiedades del PDF reader).
  - `author` — autor del documento (metadata PDF).
  - `subject` — asunto del documento (metadata PDF).
  - `default_font` — fuente por defecto para texto: `Helvetica`, `Times`, `Courier`. Por defecto `Helvetica`.
  - `margin_left`, `margin_right`, `margin_top`, `margin_bottom` — margenes del documento en milimetros. Por defecto `20.0` mm cada uno.
  - `header_text` — texto a mostrar en el header de cada pagina. Vacio = sin header.
  - `footer_text` — texto del footer. Acepta `{n}` y `{total}` como placeholders. Vacio = sin footer.
  - `page_numbers` — si `True`, incluye numero de pagina en el footer.

code:
  """PDFDoc — wrapper de documento PDF sobre fpdf2."""

  from dataclasses import dataclass, field
  from typing import Any


  @dataclass
  class PDFDoc:
      """Wrapper de documento PDF generado con fpdf2.

      Contiene el objeto FPDF interno con el estado del documento
      y la configuracion de metadatos y margenes del documento.
      La lista `pages` registra las paginas añadidas como metadata.
      """

      fpdf: Any = None            # objeto fpdf2.FPDF — estado mutable del documento
      pages: list = field(default_factory=list)  # lista de dicts con metadata de cada pagina
      title: str = ""
      author: str = ""
      subject: str = ""
      default_font: str = "Helvetica"
      margin_left: float = 20.0    # mm
      margin_right: float = 20.0   # mm
      margin_top: float = 20.0     # mm
      margin_bottom: float = 20.0  # mm
      header_text: str = ""        # texto del header recurrente (vacio = sin header)
      footer_text: str = ""        # texto del footer recurrente (vacio = sin footer)
      page_numbers: bool = False   # mostrar numeracion de pagina en footer


id: pdf_page_py_infra
name: pdf_page
lang: py
domain: infra
version: 1.0.0
algebraic: product
tags: ["pdf","page","layout","infra"]
uses_types: []
file_path: python/types/infra/pdf_page.py
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 74205184764f54038b8a4c1033f0c58f1a9dd69be695de66d84e88662eee6cc2
definition:
  @dataclass
  class PDFPage:
      width: float = 210.0
      height: float = 297.0
      orientation: str = "portrait"

description:
  Configuracion de una pagina dentro de un documento PDF. Define dimensiones y orientacion en milimetros. Por defecto A4 vertical (210 x 297 mm).

examples:
  ```python
  from pdf_page import PDFPage

  # A4 vertical (defecto)
  pagina_a4 = PDFPage()

  # A4 horizontal
  pagina_horizontal = PDFPage(width=297.0, height=210.0, orientation="landscape")

  # Carta americana
  carta = PDFPage(width=215.9, height=279.4)
  ```

notes:
  Tipo producto de solo configuracion. No tiene comportamiento — es metadata de layout. Las dimensiones se pasan a fpdf2 al llamar `add_page()`.

documentation:
  ## Campos

  - `width` — ancho de la pagina en milimetros. A4 = 210.0, Letter = 215.9. Por defecto `210.0`.
  - `height` — alto de la pagina en milimetros. A4 = 297.0, Letter = 279.4. Por defecto `297.0`.
  - `orientation` — orientacion de la pagina: `portrait` (vertical) o `landscape` (horizontal). Por defecto `portrait`.

code:
  """PDFPage — configuracion de pagina para PDF."""

  from dataclasses import dataclass


  @dataclass
  class PDFPage:
      """Configuracion de una pagina dentro de un documento PDF.

      Define dimensiones y orientacion. Las medidas son en milimetros.
      Por defecto A4 vertical (210 x 297 mm).
      """

      width: float = 210.0        # mm — A4 ancho
      height: float = 297.0       # mm — A4 alto
      orientation: str = "portrait"  # portrait, landscape


id: pdf_style_py_infra
name: pdf_style
lang: py
domain: infra
version: 1.0.0
algebraic: product
tags: ["pdf","style","typography","infra"]
uses_types: []
file_path: python/types/infra/pdf_style.py
created_at: 2026-04-13T00:06:12Z
updated_at: 2026-04-13T00:06:12Z
content_hash: 732780620cbec547dc6d23062dff1b78aa5bcfab21c900e8f837b7d52d23780f
definition:
  @dataclass
  class PDFStyle:
      font_family: str = "Helvetica"
      font_size: float = 12.0
      color: str = "#000000"
      alignment: str = "left"
      line_height: float = 1.2
      margin_top: float = 0.0
      margin_bottom: float = 2.0

description:
  Configuracion de estilo de texto para generacion de PDF. Agrupa fuente, tamaño, color, alineacion y margenes verticales para aplicar a bloques de texto y celdas de tabla.

examples:
  ```python
  from pdf_style import PDFStyle

  # Titulo grande centrado
  titulo = PDFStyle(font_family="Helvetica-Bold", font_size=22.0, alignment="center")

  # Texto de pie en gris
  pie = PDFStyle(font_size=9.0, color="#666666")

  # Cuerpo justificado con margen
  cuerpo = PDFStyle(font_size=11.0, alignment="justify", margin_bottom=4.0)
  ```

notes:
  Tipo producto inmutable por convencion. Las fuentes built-in de fpdf2 (`Helvetica`, `Times`, `Courier` y sus variantes `-Bold`, `-Oblique`, `-BoldOblique`) no requieren archivos .ttf externos. Para fuentes custom, registrarlas en fpdf2 antes de usarlas.

documentation:
  ## Campos

  - `font_family` — nombre de fuente fpdf2: `Helvetica`, `Helvetica-Bold`, `Helvetica-Oblique`, `Times`, `Courier`. Por defecto `Helvetica`.
  - `font_size` — tamaño en puntos tipograficos. Por defecto `12.0`.
  - `color` — color hex RGB (ej: `"#000000"` negro, `"#FF0000"` rojo). Por defecto negro.
  - `alignment` — alineacion horizontal del texto: `left`, `center`, `right`, `justify`. Por defecto `left`.
  - `line_height` — multiplicador de interlineado respecto al tamaño de fuente. `1.2` da un interlineado comodo.
  - `margin_top` — espacio vertical en mm antes del bloque de texto. Por defecto `0.0`.
  - `margin_bottom` — espacio vertical en mm despues del bloque de texto. Por defecto `2.0`.

code:
  """PDFStyle — configuracion de estilo de texto para PDF."""

  from dataclasses import dataclass, field


  @dataclass
  class PDFStyle:
      """Configuracion de estilo de texto para generacion de PDF.

      Agrupa fuente, tamaño, color, alineacion y margenes verticales
      para aplicar a bloques de texto y celdas de tabla.
      """

      font_family: str = "Helvetica"
      font_size: float = 12.0
      color: str = "#000000"      # hex color RGB
      alignment: str = "left"     # left, center, right, justify
      line_height: float = 1.2
      margin_top: float = 0.0     # mm de margen antes del bloque
      margin_bottom: float = 2.0  # mm de margen despues del bloque


