550c98f275
Reorganización física de crates/: - core/ (mezclaba 6 propósitos) se divide en protocol/, init/, runtime/, compat/ - shared/ (3 crates) se redistribuye en protocol/ e init/ - lapaloma (sub-módulo de ui_engine) se promueve a modules/pineal/ Renames de proyectos: - shipote → shuma (runtime de sandboxes) - nouser → akasha (explorador de Mónadas) - yahweh → nahual (motor GPUI, antes ui_engine/) - lapaloma → pineal (data-viz agnóstica) Fraccionamiento UI → core agnóstico: - vista-core (DeckState + snap, 175 LOC, 5 tests verdes) - barra-core (Task + render_html + sanitize, 90 LOC, 5 tests verdes) - vista-web y barra-web ahora son thin DOM bindings Documentación nueva: - 16 SDDs por subdirectorio (≤80 LOC c/u): protocol/init/runtime/compat + 10 módulos + apps/ - docs/STATUS.md con cifras reales por proyecto - docs/ROADMAP.md con plan a finalización (6 hitos, ~6-8 semanas) - CHANGELOG.md particionado en docs/changelog/<proyecto>.md (7 buckets) Automatización: - scripts/reorg.py — script idempotente que: git mv directorios, renombra package names, recomputa path = refs, reescribe imports rust, actualiza workspace Cargo.toml. Soporta --dry-run. - scripts/split-changelog.py — particiona CHANGELOG por componente. Validación: - cargo check --workspace pasa (124 crates + 2 nuevos cores). - 10 tests adicionales (5 en vista-core + 5 en barra-core) verdes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
317 lines
15 KiB
Markdown
317 lines
15 KiB
Markdown
# Changelog — minga (semantic_dht)
|
||
|
||
### feat(minga-explorer): listings de items recientes en cada stat card
|
||
Iter 12. Hasta ahora minga-explorer mostraba sólo counts (3
|
||
números). Ahora cada stat card muestra también un sample de los
|
||
items dentro: hashes truncados de los 5 primeros nodos AST
|
||
(con su `kind`), atestaciones (`content_hash ← did_short`) y
|
||
claves MST. Mucho más útil para debugging que el "tengo N items".
|
||
|
||
Cambios en `minga-explorer`:
|
||
- **`RepoSnapshot` extendido** con 3 nuevos `Vec<...>`:
|
||
- `recent_nodes: Vec<(String, String)>` — `(hash_short, kind)`.
|
||
- `recent_attestations: Vec<(String, String)>` —
|
||
`(content_hash_short, did_short)`.
|
||
- `recent_mst_keys: Vec<String>` — `hash_short`.
|
||
- Cap de 5 items por sección via `RECENT_LIMIT` const.
|
||
- **`load_snapshot` itera los stores** y toma los primeros 5
|
||
items via `iter().filter_map(Result::ok).take(RECENT_LIMIT)`.
|
||
Errores per-item se silencian (`filter_map`) — el dashboard
|
||
muestra lo que pueda; un par de items corruptos no debería
|
||
tirar el panel.
|
||
- **`short_hash(&str)` helper local**: trunca un hex a sus
|
||
primeros 12 chars (48 bits, distintivo dentro de un repo
|
||
single-machine).
|
||
- **`stat_card` extendido**: nuevo arg `recent_items: &[String]`.
|
||
Si no está vacío, agrega un sub-header `"recent (N de TOTAL):"`
|
||
+ una linea por item. Cada line es texto pequeño (`px(11)`)
|
||
con el color principal del theme — visualmente queda como
|
||
monospace listing aunque no use mono font (no hay todavía
|
||
en el theme).
|
||
|
||
Tests: 2 → **4** (+2 sanity de los nuevos defaults + del
|
||
`short_hash`).
|
||
|
||
Beneficio operativo:
|
||
- Después de `minga ingest archivo.rs`, el explorer muestra
|
||
inmediatamente los hashes de los nodos AST creados, qué `kind`
|
||
tienen, y las atestaciones firmadas — sin necesitar `minga
|
||
status` o queries SQL.
|
||
- "5 de 247" da contexto del crecimiento sin overwhelm de
|
||
listing completo.
|
||
|
||
Limitación documentada: los "recent" no son cronológicos — sled
|
||
ordena lexicográfico por hash. Para timeline real, agregar
|
||
timestamp al schema (cambio breaking del store, scope futuro).
|
||
|
||
### feat(minga-explorer): nueva app dashboard del repo Minga sobre stack yahweh
|
||
Iter 11. Cierra el último frente identificado: integración del
|
||
módulo Minga (VCS semántico P2P) al ecosistema GUI. Antes Minga
|
||
sólo tenía CLI (`minga init/status/ingest/listen/sync/watch`).
|
||
Ahora hay un **dashboard GPUI** que muestra los counts del repo
|
||
en vivo, sobre el mismo stack themed que las otras dos apps
|
||
explorer.
|
||
|
||
Crate nuevo `crates/apps/minga-explorer/`:
|
||
- **Deps**: `minga-store` (para `PersistentRepo::open`) +
|
||
`yahweh-theme` + `yahweh-widget-{banner,card,theme-switcher}`.
|
||
Sin `minga-cli` (no necesita prompts de passphrase) ni
|
||
`minga-core` (counts no requieren parsear AST).
|
||
- **Lectura sin passphrase**: el `PersistentRepo` se abre directo
|
||
desde `<repo>/repo` sled. Los counts (`nodes.len()`,
|
||
`attestations.len()`, `mst.len()`) son lectura pública. Para el
|
||
DID se sigue necesitando `minga status` (CLI con passphrase).
|
||
- **Refresh por polling cada 2s**: mismo pattern que
|
||
`nakui-explorer`/`nouser-explorer`.
|
||
- **3 stat cards** una por dimensión:
|
||
- Nodos AST (cyan) — fragments parseados del código.
|
||
- Atestaciones (verde) — firmas Ed25519 sobre los nodos.
|
||
- Claves MST (purple) — entradas del Merkle Search Tree.
|
||
- **Helper `stat_card(cx, label, value, description, accent, ...)`**:
|
||
fabrica una card con border-l colored + label tenue + número
|
||
grande (`px(28)`) + descripción. Reutilizable.
|
||
- **Header**: título dinámico (`Repo: <path> · reload <ms> ms`)
|
||
+ theme switcher derecha.
|
||
- **Error banner**: themed Banner::Error si el repo no abre.
|
||
- 2 tests: `load_snapshot_errors_on_missing_dir` (msg claro
|
||
cuando el dir no existe) + sanity del `RepoSnapshot::default`.
|
||
|
||
Workspace: nueva entry en `members[]`.
|
||
|
||
Smoke run del binario verificado: bootstrap completo OK, panic
|
||
esperado en open_window por falta de display.
|
||
|
||
Beneficio operativo:
|
||
- Un usuario corre `minga init` + `minga ingest archivo.rs` desde
|
||
CLI, después abre `minga-explorer` y ve los counts crecer en
|
||
vivo cuando ingiere más archivos.
|
||
- Comparte theme switcher con `nakui-explorer` y
|
||
`nouser-explorer` — cualquier preset elegido se aplica
|
||
visualmente igual cross-app.
|
||
- `minga` deja de ser sólo CLI; gana presencia GUI sin tocar
|
||
el resto del módulo.
|
||
|
||
Apps GUI integradas al stack themed: **4** (nakui-ui, nakui-explorer,
|
||
nouser-explorer, minga-explorer).
|
||
|
||
### feat(minga-core): α-hashing per-language para Python, TypeScript, JavaScript, Go
|
||
Cierra el último pendiente fundamentado del CHANGELOG. Cada lenguaje
|
||
soportado por `minga` tiene ahora su propio profile α-equivalente —
|
||
dos versiones del mismo programa que difieren sólo en nombres de
|
||
variables ligadas producen el mismo hash, no importa el lenguaje.
|
||
Refactorings tipo "rename variable" no inflan el storage del repo
|
||
en ningún dialecto.
|
||
|
||
Refactor de `alpha.rs` (639 LOC) a módulo `alpha/`:
|
||
- **`alpha/common.rs`**: primitives compartidos (TAG_*, write_kind_and_field,
|
||
emit_leaf_marker, emit_binder_body, emit_identifier_ref, push_identifier_name).
|
||
Garantiza que el formato wire del hash sea bit-equivalente entre
|
||
todos los profiles.
|
||
- **`alpha/rust.rs`**: la lógica de Rust (movida desde alpha.rs sin
|
||
cambios funcionales).
|
||
- **`alpha/python.rs`**: nuevo.
|
||
- **`alpha/ecmascript.rs`**: nuevo (cubre TypeScript + JavaScript;
|
||
comparten la mayoría de los kinds).
|
||
- **`alpha/go.rs`**: nuevo.
|
||
- **`alpha/mod.rs`**: re-exporta `hash_node_alpha` (Rust legacy) +
|
||
expone `hash_alpha_with(dialect, node)` que despacha al profile
|
||
correspondiente.
|
||
|
||
Cobertura per-language:
|
||
|
||
**Python** (`def`, `lambda`, `for`, comprehensions, `with`):
|
||
- `function_definition` y `lambda`: parámetros (incluyendo
|
||
typed_parameter, default_parameter, *args, **kwargs) introducen
|
||
binders al body. El nombre de la función NO es α-anónimo.
|
||
- `for_statement`: el `left` (identifier o tuple) introduce
|
||
binder(es) al body.
|
||
- `list_comprehension`, `set_comprehension`, `dictionary_comprehension`,
|
||
`generator_expression`: cada `for_in_clause` añade binders que
|
||
viven en el body + clauses siguientes (semántica de scope
|
||
incremental de Python).
|
||
- `with_statement`: `as` introduce binder al body (recursando en
|
||
`as_pattern_target` para llegar al identifier).
|
||
|
||
**ECMAScript** (TS + JS):
|
||
- `function_declaration`, `function_expression`, `method_definition`,
|
||
`generator_function_*`: parameters → body. Soporta TS
|
||
`required_parameter` y `optional_parameter` (`x: number`,
|
||
`x?: number`).
|
||
- `arrow_function`: tanto `(x, y) => body` como shorthand `x => body`.
|
||
- `statement_block`: `lexical_declaration` (let/const) y
|
||
`variable_declaration` (var) introducen binders al resto del block.
|
||
- `for_in_statement` (cubre `for-of` y `for-in`): `left` → body.
|
||
- `for_statement` (C-style): initializer (lexical decl) introduce
|
||
binders al condition + increment + body.
|
||
- `catch_clause`: parameter → body.
|
||
|
||
**Go**:
|
||
- `function_declaration`, `method_declaration`, `func_literal` (closure):
|
||
`parameter_list` → body. `parameter_declaration` con varios names
|
||
agrupa varios binders bajo un mismo tipo (`a, b int`).
|
||
- `block`: `short_var_declaration` (`x := ...`) introduce binders
|
||
al resto.
|
||
- `for_statement` con `range_clause` (`for k, v := range m`): los
|
||
identifiers del `left` son binders al body.
|
||
- `for_statement` con `for_clause` (C-style): initializer → body.
|
||
- `if_statement` con `initializer` (`if x := init(); x > 0`):
|
||
binders viven en condition + consequence + alternative.
|
||
|
||
API:
|
||
- `hash_alpha_with(Dialect, &SemanticNode) -> ContentHash` —
|
||
despacho per-dialect.
|
||
- `hash_node_alpha(&SemanticNode) -> ContentHash` — alias histórico
|
||
asume Rust (back-compat).
|
||
|
||
Tests: 26 nuevos en `tests/alpha_polyglot.rs`:
|
||
- Python (9): def rename, lambda rename, for-loop rename, list comp,
|
||
nested comp, with rename, function name matters, iterable name
|
||
matters, sanity negativo (operación distinta → hash distinto).
|
||
- JS/TS (9): function rename, function name matters, arrow rename,
|
||
arrow shorthand rename, let/const rename, for-of rename, classic
|
||
for rename, catch rename, TS typed param rename, TS type matters.
|
||
- Go (6): function rename, function name matters, short var decl
|
||
rename, range_clause rename, if-init rename, func_literal closure
|
||
rename.
|
||
- Cross-language (1): mismos shapes en lenguajes distintos
|
||
producen hashes distintos (sanity para evitar colisiones).
|
||
|
||
141 tests verdes en minga-core (115 antes; +26 polyglot). Refactor
|
||
sin regresión: 36 α-Rust tests siguen pasando.
|
||
|
||
Pendientes que quedan en Minga (orden de prioridad):
|
||
- `minga-vfs` FUSE (proyecto independiente, scope grande).
|
||
- Cobertura adicional por-lenguaje: Python class, JS destructuring,
|
||
Go type_switch, etc. — cada uno pequeño, no urgente.
|
||
|
||
### feat(minga-core): cierre del α-hashing de Rust — if let, while let, let-else, or-pattern, let-chains
|
||
Cierra los 5 pendientes documentados en `alpha.rs`. El hash
|
||
α-equivalente ahora es estable bajo renombre de TODOS los binders
|
||
de Rust, no sólo los del MVP (parámetros, let, for, match arms).
|
||
|
||
Pendientes cerrados:
|
||
- **`if let X = expr { ... }`**: `if_expression` detecta
|
||
`let_condition` en su `condition`, recolecta los binders del
|
||
pattern, los propaga al `consequence`. El `alternative` (else)
|
||
NO los ve.
|
||
- **`while let X = expr { ... }`**: simétrico al if-let, propaga al
|
||
`body`. El `condition` mismo se evalúa con scope previo (los
|
||
binders todavía no existen).
|
||
- **`let-else`**: `let_declaration` con campo `alternative`. El
|
||
alternative se procesa con el scope ANTES de los binders (ya
|
||
funcionaba: `feed_let` llama `feed` para no-pattern children con
|
||
el scope actual; `feed_block` extiende el scope DESPUÉS de
|
||
`feed_let`).
|
||
- **`or_pattern`**: en `pat1 | pat2` (Rust enforcement: ambos lados
|
||
introducen los mismos binders). Para emit, recorremos cada lado
|
||
con `feed_pattern`. Para collect, sólo el primer lado — iterar
|
||
todos duplicaría binders y rompería los índices de Bruijn.
|
||
- **let-chains** (`if let X = a && let Y = b { ... }`): el
|
||
`collect_let_condition_binders` recursa en el árbol del condition,
|
||
capturando todos los `let_condition` (vivan dentro de
|
||
`binary_expression` u otros nodos). Ambos binders quedan en scope
|
||
del consequence.
|
||
|
||
Helper nuevo: `feed_let_condition` para que el `pattern` del
|
||
let_condition pase por `feed_pattern` (que distingue binders vs
|
||
constructors). Sin esto, los identifiers del pattern se hasheaban
|
||
como variables libres y `Some(x)` ≠ `Some(y)` aún teniendo el
|
||
mismo significado.
|
||
|
||
Tests: 6 nuevos en `tests/alpha_invariants.rs`:
|
||
- `alpha_if_let_binder_rename_invariant`
|
||
- `alpha_if_let_else_does_not_see_binder` (sanity)
|
||
- `alpha_while_let_binder_rename_invariant`
|
||
- `alpha_let_else_binder_rename_invariant`
|
||
- `alpha_or_pattern_binder_rename_invariant`
|
||
- `alpha_let_chain_binders_propagate_to_consequence`
|
||
- `alpha_if_let_does_not_collide_with_unrelated_program` (negativo:
|
||
programas distintos NO deben dar el mismo hash)
|
||
|
||
36 tests α verdes (eran 30). 115 tests totales en minga-core.
|
||
|
||
Lo que esto significa: el hash α-equivalente de Rust en minga es
|
||
**completo** — cubre todos los constructos del lenguaje que
|
||
introducen bindings. Dos versiones del mismo programa que difieren
|
||
sólo en nombres de variables (incluyendo en `if let`, `while let`,
|
||
`or-pattern`, etc.) producen el mismo hash y por tanto la misma
|
||
identidad CAS. Refactorings del tipo "rename variable" no inflan
|
||
el storage del repo.
|
||
|
||
Pendientes futuros:
|
||
- α-hashing per-language (Python: def/lambda/comprehensions; TS/JS:
|
||
function/arrow/destructuring; Go: func/closure). Cada uno
|
||
requiere conocimiento profundo de la gramática y tests
|
||
exhaustivos. Plantilla genérica no aplica.
|
||
|
||
### feat(minga): multi-lenguaje en parser — Python, TypeScript, JavaScript, Go
|
||
Minga deja de ser Rust-only. Cualquiera de los cinco dialectos
|
||
(Rust + 4 nuevos) se ingresa al CAS por su AST normalizado, hashea
|
||
estructuralmente, sincroniza por DHT como cualquier nodo. La
|
||
auto-detección por extensión hace que `minga ingest archivo.py` o
|
||
`.ts` o `.go` "simplemente funcione".
|
||
|
||
API nueva en `minga_core::parse`:
|
||
- Funciones por dialecto (~6 LOC c/u sobre el `parse_with` común):
|
||
`python`, `typescript`, `javascript`, `go`. Más la `rust` existente.
|
||
- Enum `Dialect` con `parse(source) -> Result<SemanticNode>` y
|
||
`name() -> &'static str` para logging.
|
||
- `detect_by_extension(ext) -> Option<Dialect>`: mapea `rs`/`py`/
|
||
`pyi`/`ts`/`js`/`mjs`/`cjs`/`go` (case-insensitive). `None` para
|
||
extensiones desconocidas — el caller decide si es error o se
|
||
ignora silente.
|
||
|
||
Wire en `minga-cli`:
|
||
- `cmd_ingest` deja de hardcodear `parse::rust` — usa
|
||
`detect_dialect(file)?.parse(...)`. Acepta `.py`, `.ts`, `.js`,
|
||
`.go` además de `.rs`.
|
||
- `initial_scan` y `cmd_watch` cambian `is_rs_file` → `is_supported_source`
|
||
para incluir todas las extensiones soportadas en el filtro.
|
||
- `CliError::UnsupportedLanguage { path, extension }` nuevo, con
|
||
mensaje que lista las extensiones reconocidas.
|
||
|
||
Notas sobre hashing:
|
||
- El AST normalizado (`SemanticNode`) descarta whitespace y
|
||
comentarios — propiedad universal de tree-sitter (extras). Misma
|
||
lógica para los 5 dialectos.
|
||
- Hashing **estructural** (`cas::hash_node`) funciona para todos:
|
||
dos textos semánticamente equivalentes-por-estructura producen el
|
||
mismo hash. NO α-equivalente (las variables ligadas distinguen).
|
||
- Hashing **α-equivalente** (`alpha::hash_node_alpha`) sigue siendo
|
||
Rust-only: cada lenguaje tiene reglas distintas para qué es
|
||
binder vs. constructor (def/lambda en Python, arrow functions en
|
||
TS/JS, func + closures en Go). Implementación per-language queda
|
||
como work futuro — requiere conocimiento profundo de cada
|
||
gramática y no se plantilla genéricamente.
|
||
- Sanity test `structural_hash_distinguishes_languages` verifica
|
||
que `x = 1` parseado como Python ≠ parseado como JavaScript: las
|
||
gramáticas no comparten kinds y los hashes salen distintos.
|
||
Importante para evitar colisiones cuando el mismo source se
|
||
ingresa bajo dialectos distintos.
|
||
|
||
Deps nuevas (workspace + minga-core):
|
||
- `tree-sitter-python = "0.23"`
|
||
- `tree-sitter-typescript = "0.23"` (sólo el modo `LANGUAGE_TYPESCRIPT`,
|
||
no TSX — bumpear a TSX es agregar otro dialecto cuando se necesite).
|
||
- `tree-sitter-javascript = "0.23"`
|
||
- `tree-sitter-go = "0.23"`
|
||
|
||
Tests:
|
||
- 9 nuevos en `parse::tests`: parse básico para los 5 dialectos
|
||
(Python con type hints, TS con tipos, JS sin tipos, Go con
|
||
package declaration), `detect_by_extension` canonical +
|
||
case-insensitive, `dialect_name`, `structural_hash_distinguishes_languages`.
|
||
- 108 tests verdes en minga-core (39 → 48 unit + integration tests
|
||
pre-existentes intactos).
|
||
- 10 tests verdes en minga-cli (sin regresión en el path Rust;
|
||
el refactor a `detect_dialect`/`is_supported_source` no rompe
|
||
nada).
|
||
|
||
Pendientes futuros del changelog:
|
||
- α-hashing per-language (Python: def/lambda/comprehensions;
|
||
TS/JS: function/arrow/destructuring; Go: func/closure). Trabajo
|
||
profundo, scope independiente.
|
||
- α-Rust pendientes documentados en `alpha.rs`: `if let`,
|
||
`while let`, `let-else`, let-chains, `or_pattern` con bindings.
|
||
|