tawasuyu

tawasuyu/brahman

Rust 231TOML 70JSON 14HTML 12905 Sutichaykuna

⎇ 6cb63b266becd186f9ad016c7cd6dccc4faf855b docs/changelog/minga.md Markdown · 368 siq'ikuna · 17.5 KB Ñawpa willay ch'uya

feat(minga): minga-vfs — proyecta el repo como filesystem FUSE e77a32f4d · sergio ·

1# Changelog — minga (semantic_dht)
2
3### feat(minga-vfs): VFS FUSE — el repo como filesystem de sólo lectura
4`minga-vfs` deja de ser un stub de 2 LOC: ahora monta el repositorio
5direccionado por contenido como un filesystem FUSE navegable con
6`ls`/`cat`/`grep`/un editor, sin exponer `sled`.
7
8Layout del montaje:
9- `README` — explicación del propio VFS.
10- `roots/<hash>` — un archivo por raíz del MST (cada archivo
11  ingerido), con el código fuente **reconstruido** en formato
12  normalizado. `ls roots/` las enumera todas.
13- `cas/<hash>` — la S-expression del subárbol de cualquier hash.
14  Este directorio NO se enumera (decenas de miles de nodos) pero
15  `cat cas/<hash>` resuelve cualquier hash conocido: es el "blob
16  por hash bajo demanda" que la SDD pedía.
17
18Arquitectura, separando por capas (ver feedback de separabilidad):
19- **`render`** — `SemanticNode` → texto. Lógica pura, sin IO ni
20  FUSE. `render_source` re-imprime los tokens hoja con un
21  pretty-printer mínimo consciente de llaves (indenta tras `{`,
22  corta tras `;`); `render_sexp` vuelca el árbol literal. El render
23  de fuente es normalizado, no byte-exacto: el AST descartó
24  whitespace y comentarios al ingerir. Python, cuya estructura vive
25  en la indentación, sale como secuencia de tokens — esperado.
26- **`source`** — el contrato `NodeSource` (`roots()` + `get()`) y
27  `reconstruct()`. Backends: `RepoSource` sobre el `PersistentRepo`
28  de `sled`, `MemSource` en RAM (tests / índices efímeros).
29- **`fs`** — único módulo acoplado a `fuser`: implementa la
30  `Filesystem` trait. Inodos estáticos reservados (1-4), dinámicos
31  desde 16 con tabla `(padre, nombre) → inodo` estable. Cada
32  archivo se renderiza y cachea en el primer `lookup`.
33
34Dep nueva (workspace + minga-vfs):
35- `fuser = "0.15"` con `default-features = false`: prescinde de
36  `pkg-config`/`libfuse-dev` en build; el montaje pasa a Rust puro
37  (helper `fusermount3` en runtime).
38
39CLI: nuevo subcomando `minga mount <punto>` (`cmd_mount`), que abre
40el repo, lo envuelve en `RepoSource` y bloquea hasta
41`fusermount -u <punto>`. Pide la passphrase igual que `status`.
42
43Tests: **14** nuevos en minga-vfs (9 unit: render con llaves,
44S-expression con campos/escape, parseo de hash de 64 hex, roundtrip
45de `MemSource`; 5 integration: ingest→reconstruct lossless a nivel
46AST, `roots/` lista todo, vistas source/sexp, deduplicación de
47subárboles). Los 10 tests de minga-cli intactos (sin regresión).
48
49Pendientes:
50- `cas/` cachea contenido sin tope: navegar un repo gigante crece
51  en RAM. Un LRU sería el siguiente paso si molesta.
52- Sin extensión en los nombres (`roots/<hash>`): no guardamos el
53  lenguaje original, así que el editor no autodetecta sintaxis.
54
55### feat(minga-explorer): listings de items recientes en cada stat card
56Iter 12. Hasta ahora minga-explorer mostraba sólo counts (3
57números). Ahora cada stat card muestra también un sample de los
58items dentro: hashes truncados de los 5 primeros nodos AST
59(con su `kind`), atestaciones (`content_hash ← did_short`) y
60claves MST. Mucho más útil para debugging que el "tengo N items".
61
62Cambios en `minga-explorer`:
63- **`RepoSnapshot` extendido** con 3 nuevos `Vec<...>`:
64  - `recent_nodes: Vec<(String, String)>` — `(hash_short, kind)`.
65  - `recent_attestations: Vec<(String, String)>` —
66    `(content_hash_short, did_short)`.
67  - `recent_mst_keys: Vec<String>` — `hash_short`.
68  - Cap de 5 items por sección via `RECENT_LIMIT` const.
69- **`load_snapshot` itera los stores** y toma los primeros 5
70  items via `iter().filter_map(Result::ok).take(RECENT_LIMIT)`.
71  Errores per-item se silencian (`filter_map`) — el dashboard
72  muestra lo que pueda; un par de items corruptos no debería
73  tirar el panel.
74- **`short_hash(&str)` helper local**: trunca un hex a sus
75  primeros 12 chars (48 bits, distintivo dentro de un repo
76  single-machine).
77- **`stat_card` extendido**: nuevo arg `recent_items: &[String]`.
78  Si no está vacío, agrega un sub-header `"recent (N de TOTAL):"`
79  + una linea por item. Cada line es texto pequeño (`px(11)`)
80  con el color principal del theme — visualmente queda como
81  monospace listing aunque no use mono font (no hay todavía
82  en el theme).
83
84Tests: 2 → **4** (+2 sanity de los nuevos defaults + del
85`short_hash`).
86
87Beneficio operativo:
88- Después de `minga ingest archivo.rs`, el explorer muestra
89  inmediatamente los hashes de los nodos AST creados, qué `kind`
90  tienen, y las atestaciones firmadas — sin necesitar `minga
91  status` o queries SQL.
92- "5 de 247" da contexto del crecimiento sin overwhelm de
93  listing completo.
94
95Limitación documentada: los "recent" no son cronológicos — sled
96ordena lexicográfico por hash. Para timeline real, agregar
97timestamp al schema (cambio breaking del store, scope futuro).
98
99### feat(minga-explorer): nueva app dashboard del repo Minga sobre stack yahweh
100Iter 11. Cierra el último frente identificado: integración del
101módulo Minga (VCS semántico P2P) al ecosistema GUI. Antes Minga
102sólo tenía CLI (`minga init/status/ingest/listen/sync/watch`).
103Ahora hay un **dashboard GPUI** que muestra los counts del repo
104en vivo, sobre el mismo stack themed que las otras dos apps
105explorer.
106
107Crate nuevo `crates/apps/minga-explorer/`:
108- **Deps**: `minga-store` (para `PersistentRepo::open`) +
109  `yahweh-theme` + `yahweh-widget-{banner,card,theme-switcher}`.
110  Sin `minga-cli` (no necesita prompts de passphrase) ni
111  `minga-core` (counts no requieren parsear AST).
112- **Lectura sin passphrase**: el `PersistentRepo` se abre directo
113  desde `<repo>/repo` sled. Los counts (`nodes.len()`,
114  `attestations.len()`, `mst.len()`) son lectura pública. Para el
115  DID se sigue necesitando `minga status` (CLI con passphrase).
116- **Refresh por polling cada 2s**: mismo pattern que
117  `nakui-explorer`/`nouser-explorer`.
118- **3 stat cards** una por dimensión:
119  - Nodos AST (cyan) — fragments parseados del código.
120  - Atestaciones (verde) — firmas Ed25519 sobre los nodos.
121  - Claves MST (purple) — entradas del Merkle Search Tree.
122- **Helper `stat_card(cx, label, value, description, accent, ...)`**:
123  fabrica una card con border-l colored + label tenue + número
124  grande (`px(28)`) + descripción. Reutilizable.
125- **Header**: título dinámico (`Repo: <path> · reload <ms> ms`)
126  + theme switcher derecha.
127- **Error banner**: themed Banner::Error si el repo no abre.
128- 2 tests: `load_snapshot_errors_on_missing_dir` (msg claro
129  cuando el dir no existe) + sanity del `RepoSnapshot::default`.
130
131Workspace: nueva entry en `members[]`.
132
133Smoke run del binario verificado: bootstrap completo OK, panic
134esperado en open_window por falta de display.
135
136Beneficio operativo:
137- Un usuario corre `minga init` + `minga ingest archivo.rs` desde
138  CLI, después abre `minga-explorer` y ve los counts crecer en
139  vivo cuando ingiere más archivos.
140- Comparte theme switcher con `nakui-explorer` y
141  `nouser-explorer` — cualquier preset elegido se aplica
142  visualmente igual cross-app.
143- `minga` deja de ser sólo CLI; gana presencia GUI sin tocar
144  el resto del módulo.
145
146Apps GUI integradas al stack themed: **4** (nakui-ui, nakui-explorer,
147nouser-explorer, minga-explorer).
148
149### feat(minga-core): α-hashing per-language para Python, TypeScript, JavaScript, Go
150Cierra el último pendiente fundamentado del CHANGELOG. Cada lenguaje
151soportado por `minga` tiene ahora su propio profile α-equivalente —
152dos versiones del mismo programa que difieren sólo en nombres de
153variables ligadas producen el mismo hash, no importa el lenguaje.
154Refactorings tipo "rename variable" no inflan el storage del repo
155en ningún dialecto.
156
157Refactor de `alpha.rs` (639 LOC) a módulo `alpha/`:
158- **`alpha/common.rs`**: primitives compartidos (TAG_*, write_kind_and_field,
159  emit_leaf_marker, emit_binder_body, emit_identifier_ref, push_identifier_name).
160  Garantiza que el formato wire del hash sea bit-equivalente entre
161  todos los profiles.
162- **`alpha/rust.rs`**: la lógica de Rust (movida desde alpha.rs sin
163  cambios funcionales).
164- **`alpha/python.rs`**: nuevo.
165- **`alpha/ecmascript.rs`**: nuevo (cubre TypeScript + JavaScript;
166  comparten la mayoría de los kinds).
167- **`alpha/go.rs`**: nuevo.
168- **`alpha/mod.rs`**: re-exporta `hash_node_alpha` (Rust legacy) +
169  expone `hash_alpha_with(dialect, node)` que despacha al profile
170  correspondiente.
171
172Cobertura per-language:
173
174**Python** (`def`, `lambda`, `for`, comprehensions, `with`):
175- `function_definition` y `lambda`: parámetros (incluyendo
176  typed_parameter, default_parameter, *args, **kwargs) introducen
177  binders al body. El nombre de la función NO es α-anónimo.
178- `for_statement`: el `left` (identifier o tuple) introduce
179  binder(es) al body.
180- `list_comprehension`, `set_comprehension`, `dictionary_comprehension`,
181  `generator_expression`: cada `for_in_clause` añade binders que
182  viven en el body + clauses siguientes (semántica de scope
183  incremental de Python).
184- `with_statement`: `as` introduce binder al body (recursando en
185  `as_pattern_target` para llegar al identifier).
186
187**ECMAScript** (TS + JS):
188- `function_declaration`, `function_expression`, `method_definition`,
189  `generator_function_*`: parameters → body. Soporta TS
190  `required_parameter` y `optional_parameter` (`x: number`,
191  `x?: number`).
192- `arrow_function`: tanto `(x, y) => body` como shorthand `x => body`.
193- `statement_block`: `lexical_declaration` (let/const) y
194  `variable_declaration` (var) introducen binders al resto del block.
195- `for_in_statement` (cubre `for-of` y `for-in`): `left` → body.
196- `for_statement` (C-style): initializer (lexical decl) introduce
197  binders al condition + increment + body.
198- `catch_clause`: parameter → body.
199
200**Go**:
201- `function_declaration`, `method_declaration`, `func_literal` (closure):
202  `parameter_list` → body. `parameter_declaration` con varios names
203  agrupa varios binders bajo un mismo tipo (`a, b int`).
204- `block`: `short_var_declaration` (`x := ...`) introduce binders
205  al resto.
206- `for_statement` con `range_clause` (`for k, v := range m`): los
207  identifiers del `left` son binders al body.
208- `for_statement` con `for_clause` (C-style): initializer → body.
209- `if_statement` con `initializer` (`if x := init(); x > 0`):
210  binders viven en condition + consequence + alternative.
211
212API:
213- `hash_alpha_with(Dialect, &SemanticNode) -> ContentHash` —
214  despacho per-dialect.
215- `hash_node_alpha(&SemanticNode) -> ContentHash` — alias histórico
216  asume Rust (back-compat).
217
218Tests: 26 nuevos en `tests/alpha_polyglot.rs`:
219- Python (9): def rename, lambda rename, for-loop rename, list comp,
220  nested comp, with rename, function name matters, iterable name
221  matters, sanity negativo (operación distinta → hash distinto).
222- JS/TS (9): function rename, function name matters, arrow rename,
223  arrow shorthand rename, let/const rename, for-of rename, classic
224  for rename, catch rename, TS typed param rename, TS type matters.
225- Go (6): function rename, function name matters, short var decl
226  rename, range_clause rename, if-init rename, func_literal closure
227  rename.
228- Cross-language (1): mismos shapes en lenguajes distintos
229  producen hashes distintos (sanity para evitar colisiones).
230
231141 tests verdes en minga-core (115 antes; +26 polyglot). Refactor
232sin regresión: 36 α-Rust tests siguen pasando.
233
234Pendientes que quedan en Minga (orden de prioridad):
235- `minga-vfs` FUSE (proyecto independiente, scope grande).
236- Cobertura adicional por-lenguaje: Python class, JS destructuring,
237  Go type_switch, etc. — cada uno pequeño, no urgente.
238
239### feat(minga-core): cierre del α-hashing de Rust — if let, while let, let-else, or-pattern, let-chains
240Cierra los 5 pendientes documentados en `alpha.rs`. El hash
241α-equivalente ahora es estable bajo renombre de TODOS los binders
242de Rust, no sólo los del MVP (parámetros, let, for, match arms).
243
244Pendientes cerrados:
245- **`if let X = expr { ... }`**: `if_expression` detecta
246  `let_condition` en su `condition`, recolecta los binders del
247  pattern, los propaga al `consequence`. El `alternative` (else)
248  NO los ve.
249- **`while let X = expr { ... }`**: simétrico al if-let, propaga al
250  `body`. El `condition` mismo se evalúa con scope previo (los
251  binders todavía no existen).
252- **`let-else`**: `let_declaration` con campo `alternative`. El
253  alternative se procesa con el scope ANTES de los binders (ya
254  funcionaba: `feed_let` llama `feed` para no-pattern children con
255  el scope actual; `feed_block` extiende el scope DESPUÉS de
256  `feed_let`).
257- **`or_pattern`**: en `pat1 | pat2` (Rust enforcement: ambos lados
258  introducen los mismos binders). Para emit, recorremos cada lado
259  con `feed_pattern`. Para collect, sólo el primer lado — iterar
260  todos duplicaría binders y rompería los índices de Bruijn.
261- **let-chains** (`if let X = a && let Y = b { ... }`): el
262  `collect_let_condition_binders` recursa en el árbol del condition,
263  capturando todos los `let_condition` (vivan dentro de
264  `binary_expression` u otros nodos). Ambos binders quedan en scope
265  del consequence.
266
267Helper nuevo: `feed_let_condition` para que el `pattern` del
268let_condition pase por `feed_pattern` (que distingue binders vs
269constructors). Sin esto, los identifiers del pattern se hasheaban
270como variables libres y `Some(x)` ≠ `Some(y)` aún teniendo el
271mismo significado.
272
273Tests: 6 nuevos en `tests/alpha_invariants.rs`:
274- `alpha_if_let_binder_rename_invariant`
275- `alpha_if_let_else_does_not_see_binder` (sanity)
276- `alpha_while_let_binder_rename_invariant`
277- `alpha_let_else_binder_rename_invariant`
278- `alpha_or_pattern_binder_rename_invariant`
279- `alpha_let_chain_binders_propagate_to_consequence`
280- `alpha_if_let_does_not_collide_with_unrelated_program` (negativo:
281  programas distintos NO deben dar el mismo hash)
282
28336 tests α verdes (eran 30). 115 tests totales en minga-core.
284
285Lo que esto significa: el hash α-equivalente de Rust en minga es
286**completo** — cubre todos los constructos del lenguaje que
287introducen bindings. Dos versiones del mismo programa que difieren
288sólo en nombres de variables (incluyendo en `if let`, `while let`,
289`or-pattern`, etc.) producen el mismo hash y por tanto la misma
290identidad CAS. Refactorings del tipo "rename variable" no inflan
291el storage del repo.
292
293Pendientes futuros:
294- α-hashing per-language (Python: def/lambda/comprehensions; TS/JS:
295  function/arrow/destructuring; Go: func/closure). Cada uno
296  requiere conocimiento profundo de la gramática y tests
297  exhaustivos. Plantilla genérica no aplica.
298
299### feat(minga): multi-lenguaje en parser — Python, TypeScript, JavaScript, Go
300Minga deja de ser Rust-only. Cualquiera de los cinco dialectos
301(Rust + 4 nuevos) se ingresa al CAS por su AST normalizado, hashea
302estructuralmente, sincroniza por DHT como cualquier nodo. La
303auto-detección por extensión hace que `minga ingest archivo.py` o
304`.ts` o `.go` "simplemente funcione".
305
306API nueva en `minga_core::parse`:
307- Funciones por dialecto (~6 LOC c/u sobre el `parse_with` común):
308  `python`, `typescript`, `javascript`, `go`. Más la `rust` existente.
309- Enum `Dialect` con `parse(source) -> Result<SemanticNode>` y
310  `name() -> &'static str` para logging.
311- `detect_by_extension(ext) -> Option<Dialect>`: mapea `rs`/`py`/
312  `pyi`/`ts`/`js`/`mjs`/`cjs`/`go` (case-insensitive). `None` para
313  extensiones desconocidas — el caller decide si es error o se
314  ignora silente.
315
316Wire en `minga-cli`:
317- `cmd_ingest` deja de hardcodear `parse::rust` — usa
318  `detect_dialect(file)?.parse(...)`. Acepta `.py`, `.ts`, `.js`,
319  `.go` además de `.rs`.
320- `initial_scan` y `cmd_watch` cambian `is_rs_file` → `is_supported_source`
321  para incluir todas las extensiones soportadas en el filtro.
322- `CliError::UnsupportedLanguage { path, extension }` nuevo, con
323  mensaje que lista las extensiones reconocidas.
324
325Notas sobre hashing:
326- El AST normalizado (`SemanticNode`) descarta whitespace y
327  comentarios — propiedad universal de tree-sitter (extras). Misma
328  lógica para los 5 dialectos.
329- Hashing **estructural** (`cas::hash_node`) funciona para todos:
330  dos textos semánticamente equivalentes-por-estructura producen el
331  mismo hash. NO α-equivalente (las variables ligadas distinguen).
332- Hashing **α-equivalente** (`alpha::hash_node_alpha`) sigue siendo
333  Rust-only: cada lenguaje tiene reglas distintas para qué es
334  binder vs. constructor (def/lambda en Python, arrow functions en
335  TS/JS, func + closures en Go). Implementación per-language queda
336  como work futuro — requiere conocimiento profundo de cada
337  gramática y no se plantilla genéricamente.
338- Sanity test `structural_hash_distinguishes_languages` verifica
339  que `x = 1` parseado como Python ≠ parseado como JavaScript: las
340  gramáticas no comparten kinds y los hashes salen distintos.
341  Importante para evitar colisiones cuando el mismo source se
342  ingresa bajo dialectos distintos.
343
344Deps nuevas (workspace + minga-core):
345- `tree-sitter-python = "0.23"`
346- `tree-sitter-typescript = "0.23"` (sólo el modo `LANGUAGE_TYPESCRIPT`,
347  no TSX — bumpear a TSX es agregar otro dialecto cuando se necesite).
348- `tree-sitter-javascript = "0.23"`
349- `tree-sitter-go = "0.23"`
350
351Tests:
352- 9 nuevos en `parse::tests`: parse básico para los 5 dialectos
353  (Python con type hints, TS con tipos, JS sin tipos, Go con
354  package declaration), `detect_by_extension` canonical +
355  case-insensitive, `dialect_name`, `structural_hash_distinguishes_languages`.
356- 108 tests verdes en minga-core (39 → 48 unit + integration tests
357  pre-existentes intactos).
358- 10 tests verdes en minga-cli (sin regresión en el path Rust;
359  el refactor a `detect_dialect`/`is_supported_source` no rompe
360  nada).
361
362Pendientes futuros del changelog:
363- α-hashing per-language (Python: def/lambda/comprehensions;
364  TS/JS: function/arrow/destructuring; Go: func/closure). Trabajo
365  profundo, scope independiente.
366- α-Rust pendientes documentados en `alpha.rs`: `if let`,
367  `while let`, `let-else`, let-chains, `or_pattern` con bindings.
368

minga.md

Changelog — minga (semantic_dht)

feat(minga-vfs): VFS FUSE — el repo como filesystem de sólo lectura

minga-vfs deja de ser un stub de 2 LOC: ahora monta el repositorio direccionado por contenido como un filesystem FUSE navegable con ls/cat/grep/un editor, sin exponer sled.

Layout del montaje:

Arquitectura, separando por capas (ver feedback de separabilidad):

Dep nueva (workspace + minga-vfs):

CLI: nuevo subcomando minga mount <punto> (cmd_mount), que abre el repo, lo envuelve en RepoSource y bloquea hasta fusermount -u <punto>. Pide la passphrase igual que status.

Tests: 14 nuevos en minga-vfs (9 unit: render con llaves, S-expression con campos/escape, parseo de hash de 64 hex, roundtrip de MemSource; 5 integration: ingest→reconstruct lossless a nivel AST, roots/ lista todo, vistas source/sexp, deduplicación de subárboles). Los 10 tests de minga-cli intactos (sin regresión).

Pendientes:

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:

Tests: 2 → 4 (+2 sanity de los nuevos defaults + del short_hash).

Beneficio operativo:

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/:

Workspace: nueva entry en members[].

Smoke run del binario verificado: bootstrap completo OK, panic esperado en open_window por falta de display.

Beneficio operativo:

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/:

Cobertura per-language:

Python (def, lambda, for, comprehensions, with):

ECMAScript (TS + JS):

Go:

API:

Tests: 26 nuevos en tests/alpha_polyglot.rs:

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):

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:

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:

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:

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:

Wire en minga-cli:

Notas sobre hashing:

Deps nuevas (workspace + minga-core):

Tests:

Pendientes futuros del changelog: