nexora-lang 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. nexora_lang-0.10.0/CHANGELOG.md +913 -0
  2. nexora_lang-0.10.0/CODE_OF_CONDUCT.md +51 -0
  3. nexora_lang-0.10.0/CONTRIBUTING.md +210 -0
  4. nexora_lang-0.10.0/LICENSE +21 -0
  5. nexora_lang-0.10.0/MANIFEST.in +6 -0
  6. nexora_lang-0.10.0/PKG-INFO +314 -0
  7. nexora_lang-0.10.0/README.md +287 -0
  8. nexora_lang-0.10.0/SECURITY.md +99 -0
  9. nexora_lang-0.10.0/docs/ai-context.md +918 -0
  10. nexora_lang-0.10.0/docs/ai-tooling.md +405 -0
  11. nexora_lang-0.10.0/docs/architecture.md +218 -0
  12. nexora_lang-0.10.0/docs/cli-reference.md +286 -0
  13. nexora_lang-0.10.0/docs/contracts.md +468 -0
  14. nexora_lang-0.10.0/docs/decisions/0001-tipos-estaticos-sin-conversiones-implicitas.md +57 -0
  15. nexora_lang-0.10.0/docs/decisions/0002-el-interprete-solo-ejecuta-programas-comprobados.md +47 -0
  16. nexora_lang-0.10.0/docs/decisions/0003-funciones-retorno-e-if.md +54 -0
  17. nexora_lang-0.10.0/docs/decisions/0004-contratos-en-ejecucion.md +56 -0
  18. nexora_lang-0.10.0/docs/decisions/0005-diagnosticos-y-esquemas-json.md +49 -0
  19. nexora_lang-0.10.0/docs/decisions/0006-requisitos-verificables.md +212 -0
  20. nexora_lang-0.10.0/docs/decisions/0007-contratos-estables-y-tipos-refinados.md +125 -0
  21. nexora_lang-0.10.0/docs/decisions/0008-ejemplos-y-nex-test.md +872 -0
  22. nexora_lang-0.10.0/docs/decisions/0009-memoria-agotada.md +312 -0
  23. nexora_lang-0.10.0/docs/decisions/0010-var-y-while.md +376 -0
  24. nexora_lang-0.10.0/docs/decisions/0011-operaciones-sobre-textos.md +825 -0
  25. nexora_lang-0.10.0/docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md +1100 -0
  26. nexora_lang-0.10.0/docs/decisions/0013-listas-y-for.md +314 -0
  27. nexora_lang-0.10.0/docs/decisions/0014-registros-nominales-inmutables.md +467 -0
  28. nexora_lang-0.10.0/docs/decisions/0015-entrada-argumentos-archivos-y-permisos.md +306 -0
  29. nexora_lang-0.10.0/docs/decisions/0016-option-y-result.md +202 -0
  30. nexora_lang-0.10.0/docs/decisions/README.md +36 -0
  31. nexora_lang-0.10.0/docs/diagnostics.md +272 -0
  32. nexora_lang-0.10.0/docs/functions.md +379 -0
  33. nexora_lang-0.10.0/docs/getting-started.md +364 -0
  34. nexora_lang-0.10.0/docs/index.md +61 -0
  35. nexora_lang-0.10.0/docs/inspect-schema.md +217 -0
  36. nexora_lang-0.10.0/docs/installation.md +194 -0
  37. nexora_lang-0.10.0/docs/json-protocol.md +248 -0
  38. nexora_lang-0.10.0/docs/language-spec.md +1962 -0
  39. nexora_lang-0.10.0/docs/limitations.md +154 -0
  40. nexora_lang-0.10.0/docs/lsp.md +74 -0
  41. nexora_lang-0.10.0/docs/mcp.md +242 -0
  42. nexora_lang-0.10.0/docs/playground.md +906 -0
  43. nexora_lang-0.10.0/docs/roadmap.md +134 -0
  44. nexora_lang-0.10.0/docs/schemas/nexora-capabilities.schema.json +176 -0
  45. nexora_lang-0.10.0/docs/schemas/nexora-context.schema.json +646 -0
  46. nexora_lang-0.10.0/docs/schemas/nexora-diagnostics.schema.json +158 -0
  47. nexora_lang-0.10.0/docs/schemas/nexora-inspect.schema.json +2105 -0
  48. nexora_lang-0.10.0/docs/schemas/nexora-result.schema.json +174 -0
  49. nexora_lang-0.10.0/docs/schemas/nexora-spec.schema.json +90 -0
  50. nexora_lang-0.10.0/docs/schemas/nexora-symbols.schema.json +372 -0
  51. nexora_lang-0.10.0/docs/schemas/nexora-test.schema.json +380 -0
  52. nexora_lang-0.10.0/docs/testing.md +770 -0
  53. nexora_lang-0.10.0/docs/types.md +657 -0
  54. nexora_lang-0.10.0/examples/README.md +61 -0
  55. nexora_lang-0.10.0/examples/arithmetic.nex +37 -0
  56. nexora_lang-0.10.0/examples/checked_examples.nex +26 -0
  57. nexora_lang-0.10.0/examples/console.nex +24 -0
  58. nexora_lang-0.10.0/examples/contracts.nex +34 -0
  59. nexora_lang-0.10.0/examples/failing/contract_violation.nex +18 -0
  60. nexora_lang-0.10.0/examples/failing/loop_measure.nex +22 -0
  61. nexora_lang-0.10.0/examples/failing/parse_number.nex +12 -0
  62. nexora_lang-0.10.0/examples/failing/refinement_violation.nex +16 -0
  63. nexora_lang-0.10.0/examples/failing/runtime_error.nex +10 -0
  64. nexora_lang-0.10.0/examples/files.nex +20 -0
  65. nexora_lang-0.10.0/examples/functions.nex +68 -0
  66. nexora_lang-0.10.0/examples/hello.nex +5 -0
  67. nexora_lang-0.10.0/examples/invalid/name_errors.nex +24 -0
  68. nexora_lang-0.10.0/examples/invalid/refinement_errors.nex +23 -0
  69. nexora_lang-0.10.0/examples/invalid/syntax_error.nex +16 -0
  70. nexora_lang-0.10.0/examples/invalid/type_errors.nex +26 -0
  71. nexora_lang-0.10.0/examples/lists.nex +53 -0
  72. nexora_lang-0.10.0/examples/loops.nex +61 -0
  73. nexora_lang-0.10.0/examples/option_result.nex +57 -0
  74. nexora_lang-0.10.0/examples/records.nex +54 -0
  75. nexora_lang-0.10.0/examples/refined_types.nex +36 -0
  76. nexora_lang-0.10.0/examples/texts.nex +63 -0
  77. nexora_lang-0.10.0/examples/types.nex +40 -0
  78. nexora_lang-0.10.0/examples/variables.nex +42 -0
  79. nexora_lang-0.10.0/pyproject.toml +77 -0
  80. nexora_lang-0.10.0/setup.cfg +4 -0
  81. nexora_lang-0.10.0/src/nexora/__init__.py +5 -0
  82. nexora_lang-0.10.0/src/nexora/__main__.py +5 -0
  83. nexora_lang-0.10.0/src/nexora/api.py +333 -0
  84. nexora_lang-0.10.0/src/nexora/ast_nodes.py +361 -0
  85. nexora_lang-0.10.0/src/nexora/capabilities.py +189 -0
  86. nexora_lang-0.10.0/src/nexora/checker.py +2174 -0
  87. nexora_lang-0.10.0/src/nexora/cli.py +475 -0
  88. nexora_lang-0.10.0/src/nexora/diagnostics.py +229 -0
  89. nexora_lang-0.10.0/src/nexora/host.py +93 -0
  90. nexora_lang-0.10.0/src/nexora/interpreter.py +887 -0
  91. nexora_lang-0.10.0/src/nexora/lexer.py +280 -0
  92. nexora_lang-0.10.0/src/nexora/mcp_server.py +170 -0
  93. nexora_lang-0.10.0/src/nexora/parser.py +1026 -0
  94. nexora_lang-0.10.0/src/nexora/pipeline.py +73 -0
  95. nexora_lang-0.10.0/src/nexora/prelude.py +185 -0
  96. nexora_lang-0.10.0/src/nexora/schemas.py +482 -0
  97. nexora_lang-0.10.0/src/nexora/tokens.py +127 -0
  98. nexora_lang-0.10.0/src/nexora/tooling.py +697 -0
  99. nexora_lang-0.10.0/src/nexora/type_adapter.py +281 -0
  100. nexora_lang-0.10.0/src/nexora/types.py +467 -0
  101. nexora_lang-0.10.0/src/nexora/values.py +113 -0
  102. nexora_lang-0.10.0/src/nexora/version.py +3 -0
  103. nexora_lang-0.10.0/src/nexora/worker.py +144 -0
  104. nexora_lang-0.10.0/src/nexora_lang.egg-info/PKG-INFO +314 -0
  105. nexora_lang-0.10.0/src/nexora_lang.egg-info/SOURCES.txt +238 -0
  106. nexora_lang-0.10.0/src/nexora_lang.egg-info/dependency_links.txt +1 -0
  107. nexora_lang-0.10.0/src/nexora_lang.egg-info/entry_points.txt +3 -0
  108. nexora_lang-0.10.0/src/nexora_lang.egg-info/requires.txt +10 -0
  109. nexora_lang-0.10.0/src/nexora_lang.egg-info/top_level.txt +1 -0
  110. nexora_lang-0.10.0/tests/golden/examples/arithmetic.check.stderr +0 -0
  111. nexora_lang-0.10.0/tests/golden/examples/arithmetic.run.stderr +0 -0
  112. nexora_lang-0.10.0/tests/golden/examples/arithmetic.run.stdout +17 -0
  113. nexora_lang-0.10.0/tests/golden/examples/checked_examples.check.stderr +0 -0
  114. nexora_lang-0.10.0/tests/golden/examples/checked_examples.run.stderr +0 -0
  115. nexora_lang-0.10.0/tests/golden/examples/checked_examples.run.stdout +1 -0
  116. nexora_lang-0.10.0/tests/golden/examples/checked_examples.test.stderr +0 -0
  117. nexora_lang-0.10.0/tests/golden/examples/checked_examples.test.stdout +4 -0
  118. nexora_lang-0.10.0/tests/golden/examples/console.check.stderr +0 -0
  119. nexora_lang-0.10.0/tests/golden/examples/console.run.stderr +0 -0
  120. nexora_lang-0.10.0/tests/golden/examples/console.run.stdout +1 -0
  121. nexora_lang-0.10.0/tests/golden/examples/contracts.check.stderr +0 -0
  122. nexora_lang-0.10.0/tests/golden/examples/contracts.run.stderr +0 -0
  123. nexora_lang-0.10.0/tests/golden/examples/contracts.run.stdout +3 -0
  124. nexora_lang-0.10.0/tests/golden/examples/failing__contract_violation.check.stderr +0 -0
  125. nexora_lang-0.10.0/tests/golden/examples/failing__contract_violation.run.stderr +10 -0
  126. nexora_lang-0.10.0/tests/golden/examples/failing__contract_violation.run.stdout +1 -0
  127. nexora_lang-0.10.0/tests/golden/examples/failing__loop_measure.check.stderr +0 -0
  128. nexora_lang-0.10.0/tests/golden/examples/failing__loop_measure.run.stderr +12 -0
  129. nexora_lang-0.10.0/tests/golden/examples/failing__loop_measure.run.stdout +0 -0
  130. nexora_lang-0.10.0/tests/golden/examples/failing__parse_number.check.stderr +0 -0
  131. nexora_lang-0.10.0/tests/golden/examples/failing__parse_number.run.stderr +12 -0
  132. nexora_lang-0.10.0/tests/golden/examples/failing__parse_number.run.stdout +1 -0
  133. nexora_lang-0.10.0/tests/golden/examples/failing__refinement_violation.check.stderr +0 -0
  134. nexora_lang-0.10.0/tests/golden/examples/failing__refinement_violation.run.stderr +11 -0
  135. nexora_lang-0.10.0/tests/golden/examples/failing__refinement_violation.run.stdout +1 -0
  136. nexora_lang-0.10.0/tests/golden/examples/failing__runtime_error.check.stderr +0 -0
  137. nexora_lang-0.10.0/tests/golden/examples/failing__runtime_error.run.stderr +7 -0
  138. nexora_lang-0.10.0/tests/golden/examples/failing__runtime_error.run.stdout +1 -0
  139. nexora_lang-0.10.0/tests/golden/examples/files.check.stderr +0 -0
  140. nexora_lang-0.10.0/tests/golden/examples/files.run.stderr +0 -0
  141. nexora_lang-0.10.0/tests/golden/examples/files.run.stdout +3 -0
  142. nexora_lang-0.10.0/tests/golden/examples/functions.check.stderr +0 -0
  143. nexora_lang-0.10.0/tests/golden/examples/functions.run.stderr +0 -0
  144. nexora_lang-0.10.0/tests/golden/examples/functions.run.stdout +9 -0
  145. nexora_lang-0.10.0/tests/golden/examples/hello.check.stderr +0 -0
  146. nexora_lang-0.10.0/tests/golden/examples/hello.run.stderr +0 -0
  147. nexora_lang-0.10.0/tests/golden/examples/hello.run.stdout +1 -0
  148. nexora_lang-0.10.0/tests/golden/examples/invalid__name_errors.check.stderr +65 -0
  149. nexora_lang-0.10.0/tests/golden/examples/invalid__name_errors.run.stderr +65 -0
  150. nexora_lang-0.10.0/tests/golden/examples/invalid__name_errors.run.stdout +0 -0
  151. nexora_lang-0.10.0/tests/golden/examples/invalid__refinement_errors.check.stderr +47 -0
  152. nexora_lang-0.10.0/tests/golden/examples/invalid__refinement_errors.run.stderr +47 -0
  153. nexora_lang-0.10.0/tests/golden/examples/invalid__refinement_errors.run.stdout +0 -0
  154. nexora_lang-0.10.0/tests/golden/examples/invalid__syntax_error.check.stderr +41 -0
  155. nexora_lang-0.10.0/tests/golden/examples/invalid__syntax_error.run.stderr +41 -0
  156. nexora_lang-0.10.0/tests/golden/examples/invalid__syntax_error.run.stdout +0 -0
  157. nexora_lang-0.10.0/tests/golden/examples/invalid__type_errors.check.stderr +79 -0
  158. nexora_lang-0.10.0/tests/golden/examples/invalid__type_errors.run.stderr +79 -0
  159. nexora_lang-0.10.0/tests/golden/examples/invalid__type_errors.run.stdout +0 -0
  160. nexora_lang-0.10.0/tests/golden/examples/lists.check.stderr +0 -0
  161. nexora_lang-0.10.0/tests/golden/examples/lists.run.stderr +0 -0
  162. nexora_lang-0.10.0/tests/golden/examples/lists.run.stdout +12 -0
  163. nexora_lang-0.10.0/tests/golden/examples/loops.check.stderr +0 -0
  164. nexora_lang-0.10.0/tests/golden/examples/loops.run.stderr +0 -0
  165. nexora_lang-0.10.0/tests/golden/examples/loops.run.stdout +7 -0
  166. nexora_lang-0.10.0/tests/golden/examples/loops.test.stderr +0 -0
  167. nexora_lang-0.10.0/tests/golden/examples/loops.test.stdout +5 -0
  168. nexora_lang-0.10.0/tests/golden/examples/option_result.check.stderr +0 -0
  169. nexora_lang-0.10.0/tests/golden/examples/option_result.run.stderr +0 -0
  170. nexora_lang-0.10.0/tests/golden/examples/option_result.run.stdout +14 -0
  171. nexora_lang-0.10.0/tests/golden/examples/records.check.stderr +0 -0
  172. nexora_lang-0.10.0/tests/golden/examples/records.run.stderr +0 -0
  173. nexora_lang-0.10.0/tests/golden/examples/records.run.stdout +11 -0
  174. nexora_lang-0.10.0/tests/golden/examples/refined_types.check.stderr +0 -0
  175. nexora_lang-0.10.0/tests/golden/examples/refined_types.run.stderr +0 -0
  176. nexora_lang-0.10.0/tests/golden/examples/refined_types.run.stdout +3 -0
  177. nexora_lang-0.10.0/tests/golden/examples/texts.check.stderr +0 -0
  178. nexora_lang-0.10.0/tests/golden/examples/texts.run.stderr +0 -0
  179. nexora_lang-0.10.0/tests/golden/examples/texts.run.stdout +14 -0
  180. nexora_lang-0.10.0/tests/golden/examples/texts.test.stderr +0 -0
  181. nexora_lang-0.10.0/tests/golden/examples/texts.test.stdout +4 -0
  182. nexora_lang-0.10.0/tests/golden/examples/types.check.stderr +0 -0
  183. nexora_lang-0.10.0/tests/golden/examples/types.run.stderr +0 -0
  184. nexora_lang-0.10.0/tests/golden/examples/types.run.stdout +14 -0
  185. nexora_lang-0.10.0/tests/golden/examples/variables.check.stderr +0 -0
  186. nexora_lang-0.10.0/tests/golden/examples/variables.run.stderr +0 -0
  187. nexora_lang-0.10.0/tests/golden/examples/variables.run.stdout +15 -0
  188. nexora_lang-0.10.0/tests/golden/schemas/nexora-inspect-0.3.1.schema.json +1160 -0
  189. nexora_lang-0.10.0/tests/golden/schemas/nexora-inspect-0.4.0.schema.json +1226 -0
  190. nexora_lang-0.10.0/tests/golden/schemas/nexora-inspect-0.5.1.schema.json +1418 -0
  191. nexora_lang-0.10.0/tests/helpers.py +85 -0
  192. nexora_lang-0.10.0/tests/test_api.py +203 -0
  193. nexora_lang-0.10.0/tests/test_checker.py +190 -0
  194. nexora_lang-0.10.0/tests/test_cli.py +643 -0
  195. nexora_lang-0.10.0/tests/test_contracts.py +289 -0
  196. nexora_lang-0.10.0/tests/test_diagnostics.py +133 -0
  197. nexora_lang-0.10.0/tests/test_docs.py +311 -0
  198. nexora_lang-0.10.0/tests/test_example_clauses.py +298 -0
  199. nexora_lang-0.10.0/tests/test_examples.py +178 -0
  200. nexora_lang-0.10.0/tests/test_for.py +266 -0
  201. nexora_lang-0.10.0/tests/test_functions.py +314 -0
  202. nexora_lang-0.10.0/tests/test_input_arguments.py +94 -0
  203. nexora_lang-0.10.0/tests/test_input_stdin.py +121 -0
  204. nexora_lang-0.10.0/tests/test_inspect.py +206 -0
  205. nexora_lang-0.10.0/tests/test_interpreter.py +152 -0
  206. nexora_lang-0.10.0/tests/test_lexer.py +207 -0
  207. nexora_lang-0.10.0/tests/test_loops.py +555 -0
  208. nexora_lang-0.10.0/tests/test_mcp.py +244 -0
  209. nexora_lang-0.10.0/tests/test_nex_test.py +509 -0
  210. nexora_lang-0.10.0/tests/test_option_result.py +388 -0
  211. nexora_lang-0.10.0/tests/test_out_of_memory.py +298 -0
  212. nexora_lang-0.10.0/tests/test_parser.py +264 -0
  213. nexora_lang-0.10.0/tests/test_permissions_environments.py +126 -0
  214. nexora_lang-0.10.0/tests/test_permissions_files.py +314 -0
  215. nexora_lang-0.10.0/tests/test_permissions_static.py +88 -0
  216. nexora_lang-0.10.0/tests/test_records_diagnostics.py +97 -0
  217. nexora_lang-0.10.0/tests/test_records_syntax.py +170 -0
  218. nexora_lang-0.10.0/tests/test_records_tools.py +97 -0
  219. nexora_lang-0.10.0/tests/test_records_types.py +201 -0
  220. nexora_lang-0.10.0/tests/test_records_values.py +162 -0
  221. nexora_lang-0.10.0/tests/test_refined_types.py +408 -0
  222. nexora_lang-0.10.0/tests/test_robustness.py +237 -0
  223. nexora_lang-0.10.0/tests/test_security.py +209 -0
  224. nexora_lang-0.10.0/tests/test_text_operations.py +576 -0
  225. nexora_lang-0.10.0/tests/test_tooling.py +567 -0
  226. nexora_lang-0.10.0/tests/test_tooling_tasks.py +48 -0
  227. nexora_lang-0.10.0/tests/test_type_adapter.py +770 -0
  228. nexora_lang-0.10.0/tests/test_type_b4.py +389 -0
  229. nexora_lang-0.10.0/tests/test_type_b5.py +408 -0
  230. nexora_lang-0.10.0/tests/test_type_b6.py +432 -0
  231. nexora_lang-0.10.0/tests/test_type_b7.py +510 -0
  232. nexora_lang-0.10.0/tests/test_type_b8.py +612 -0
  233. nexora_lang-0.10.0/tests/test_type_lists.py +258 -0
  234. nexora_lang-0.10.0/tests/test_type_lists_functions.py +254 -0
  235. nexora_lang-0.10.0/tests/test_type_lists_syntax.py +188 -0
  236. nexora_lang-0.10.0/tests/test_type_lists_values.py +258 -0
  237. nexora_lang-0.10.0/tests/test_type_model.py +327 -0
  238. nexora_lang-0.10.0/tests/test_types.py +244 -0
  239. nexora_lang-0.10.0/tests/test_worker.py +141 -0
  240. nexora_lang-0.10.0/tests/tooling_tasks.py +125 -0
@@ -0,0 +1,913 @@
1
+ # Cambios
2
+
3
+ Registro de los cambios de NEXORA, con la versión más reciente primero.
4
+
5
+ Todas las versiones son anteriores a la 1.0: el lenguaje puede cambiar de forma incompatible
6
+ entre versiones. Cada cambio incompatible se indica aquí y se explica en `docs/decisions/`.
7
+
8
+ Este registro empieza con la versión 0.1.0, la primera que pudo ejecutarse. No hay versiones
9
+ publicadas en ningún repositorio de paquetes: los números identifican estados del código.
10
+
11
+ Cada versión agrupa sus cambios en estas secciones, cuando tienen contenido:
12
+
13
+ | Sección | Qué recoge |
14
+ |---------|------------|
15
+ | Añadido | Funcionalidades nuevas |
16
+ | Corregido | Defectos resueltos |
17
+ | Cambios incompatibles | Lo que puede hacer que un programa o un script existente deje de funcionar, y cómo actualizarlo |
18
+ | Retirado | Lo que deja de existir |
19
+ | Documentación | Cambios relevantes en la documentación |
20
+ | Limitaciones | Restricciones conocidas que conviene tener presentes |
21
+
22
+ ## 0.10.0 — Option y Result
23
+
24
+ Estado del repositorio, no una versión publicada: todavía no hay tag ni paquete de esta versión en
25
+ ningún registro. Esta versión implementa la
26
+ [decisión 0016](docs/decisions/0016-option-y-result.md), que da al lenguaje los dos tipos que
27
+ permiten decir que **no hay valor** y que **algo falló**, sin excepciones.
28
+
29
+ ### Añadido
30
+
31
+ - **`Option[T]`**, con sus dos variantes: `Some(valor)` y `None`. `Option.is_some` y
32
+ `Option.is_none` dicen cuál es; `Option.unwrap_or` devuelve el valor o un defecto del mismo tipo, y
33
+ `Option.unwrap` lo lee sin defecto, con el diagnóstico `E3011` si no lo hay.
34
+ - **`Result[T, E]`**, con `Ok(valor)` y `Err(error)`. `Result.is_ok` y `Result.is_err` dicen cuál es;
35
+ `Result.unwrap_or` devuelve el valor o un defecto, y `Result.unwrap` y `Result.error` lo leen sin
36
+ defecto, con los diagnósticos `E3012` y `E3013` cuando la variante no lo lleva. **El error se
37
+ guarda tal cual**: `Err("falta el archivo")` sigue siendo ese texto al leerlo.
38
+ - **Los tipos `Option[T]` y `Result[T, E]` se pueden escribir**, y anidarse: `Option[Option[Int]]`,
39
+ `Result[Text, Error]`.
40
+ - **Un ejemplo nuevo**, `examples/option_result.nex`, con una búsqueda opcional y un análisis que
41
+ puede fallar.
42
+
43
+ ### Cambios incompatibles
44
+
45
+ - Los nombres `Option`, `Result`, `Some`, `Ok` y `Err` pasan a estar en el lenguaje. Un programa que
46
+ declarara una función con uno de esos nombres usaría la integrada, como ya pasaba con `print` o
47
+ `to_text`. `None` es el caso contrario: si el programa declara un nombre así, el suyo gana.
48
+ - Los tipos con argumentos escribibles pasan de ser solo `List[T]` a ser también `Option[T]` y
49
+ `Result[T, E]`.
50
+ - La especificación pasa a la versión 0.10 y la implementación de referencia a la 0.10.0. La 0.9.0 ya
51
+ publicada en npm no se toca.
52
+
53
+ ### Documentación
54
+
55
+ - La especificación describe los dos tipos en la sección 4.7 y sus funciones en la 8.4.
56
+ - `docs/types.md` tiene una sección nueva con los dos tipos, cuándo usar cada uno y lo que no hay.
57
+ - Nueva [ADR 0016](docs/decisions/0016-option-y-result.md), que explica la decisión y por qué la
58
+ propagación `?` no se implementa ahora.
59
+
60
+ ### Limitaciones
61
+
62
+ - No hay `match`, ni `if let`, ni el operador `?`: las variantes se reconocen con funciones y el
63
+ valor se lee con `unwrap_or` cuando se sabe cuál es. La 0012 sigue siendo propuesta en los enums
64
+ del usuario y el `match`.
65
+ - El tipo de un `None`, de un `Ok` o de un `Err` escrito directamente en una comparación no se
66
+ deduce: hay que escribir antes el tipo del resultado en una variable.
67
+
68
+ ## 0.9.0 — Entrada, argumentos, archivos y permisos
69
+
70
+ Estado del repositorio, no una versión publicada: no hay tag ni paquete en ningún repositorio. Esta
71
+ versión implementa la [decisión 0015](docs/decisions/0015-entrada-argumentos-archivos-y-permisos.md),
72
+ que describe la entrada de un programa, sus argumentos y el acceso a los archivos con permisos
73
+ explícitos. La [0012](docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md) no se
74
+ reescribe: su parte de entrada (D11) y su sistema de efectos siguen siendo propuesta, y esta
75
+ versión decide otra forma de hacer lo mismo.
76
+
77
+ ### Añadido
78
+
79
+ - **Los argumentos del programa**: `args() -> List[Text]` devuelve los argumentos con los que se
80
+ ejecutó, escritos tras `--`: `nex run programa.nex -- uno dos`. No necesitan ninguna declaración.
81
+ - **La entrada estándar**: `input() -> Text` lee una línea, sin el final de línea. Cuando ya no
82
+ queda nada que leer, el programa se detiene con `E3009`. No necesita ninguna declaración.
83
+ - **Los archivos**: `read_file(ruta)` y `write_file(ruta, texto)`. Un programa declara lo que
84
+ necesita con `uses read "ruta"` o `uses write "ruta"`, y quien lo ejecuta tiene que permitirlo
85
+ con `--allow-read=RUTA` o `--allow-write=RUTA`.
86
+ - **El modelo de permisos**: la capacidad efectiva es la intersección de lo que declara el
87
+ programa, lo que concede quien lo ejecuta y lo que permite el entorno. Declarar no concede,
88
+ conceder no habilita, y el entorno manda. Las rutas se confinan sobre la ruta resuelta: un `..` o
89
+ un enlace simbólico que salen del ámbito concedido no sirven de nada.
90
+ - **Herramientas**: `nexora.context/1` añade el campo `capabilities`, con lo que el programa
91
+ declara; `nexora.inspect/2` añade el nodo `UsesDecl` y el campo `uses` en `Program`. Los números
92
+ de los formatos no cambian.
93
+
94
+ ### Cambiado
95
+
96
+ - Solo en programas que ya eran inválidos: `uses` pasa de palabra reservada a palabra clave, y
97
+ `read`, `write`, `args`, `input`, `read_file` y `write_file` pasan a ser nombres predefinidos.
98
+ - La versión del repositorio pasa a `0.9.0` y la especificación a `0.9`. Los esquemas JSON no
99
+ cambian de versión: los campos y nodos nuevos son aditivos.
100
+
101
+ ### Documentación
102
+
103
+ - La [especificación](docs/language-spec.md) pasa a la versión 0.9 y documenta la entrada, los
104
+ argumentos y los archivos (sección 8.3), con la gramática, los permisos y las rutas.
105
+ - Nuevo [ADR 0015](docs/decisions/0015-entrada-argumentos-archivos-y-permisos.md).
106
+ - Actualizados `diagnostics.md`, `limitations.md`, `cli-reference.md`, `playground.md`,
107
+ `architecture.md`, `roadmap.md`, `types.md`, `json-protocol.md`, `inspect-schema.md` y `README.md`.
108
+
109
+ ### Limitaciones
110
+
111
+ - No se puede leer «hasta el final» de la entrada estándar sin parar: `input()` falla con `E3009`.
112
+ - Solo se leen y escriben archivos de texto: no hay directorios, ni binarios, ni renombrar, ni
113
+ borrar.
114
+ - No hay red, ni procesos externos, ni variables de entorno.
115
+ - El playground y el servidor MCP no admiten archivos: el worker no tiene ningún campo en su
116
+ protocolo para concederlos.
117
+
118
+ ## 0.8.0 — Registros nominales inmutables
119
+
120
+ Estado del repositorio, no una versión publicada: no hay tag ni paquete en ningún repositorio. Esta
121
+ versión implementa la [decisión 0014](docs/decisions/0014-registros-nominales-inmutables.md), que
122
+ acepta la parte de D02 (registros nominales frente a estructurales) de la propuesta
123
+ [0012](docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md) en el alcance
124
+ realmente implementado. El resto de la 0012 sigue siendo una propuesta y no forma parte de esta
125
+ versión.
126
+
127
+ ### Añadido
128
+
129
+ - **Registros**: un tipo que declara el programa, con campos inmutables y obligatorios:
130
+ `type Point = { x: Int, y: Int }`. Se construyen con los campos por nombre
131
+ (`Point(x: 1, y: 2)`, en cualquier orden) y sus campos se leen con `p.x`, también en cadena
132
+ (`p.x.y`). Un registro es **nominal**: su identidad es la de su declaración, no la de su nombre
133
+ ni la de sus campos, así que dos registros con los mismos campos son tipos distintos (`E2011`) y
134
+ dos valores del mismo registro se comparan por valor.
135
+ - **Identidad nominal en el modelo de tipos** (`types.py`): la variante `Nominal` y el token opaco
136
+ `RecordIdentity`, que el comprobador crea una vez por declaración y que viaja en el
137
+ `CheckedProgram` hasta el intérprete. El intérprete no la reconstruye a partir del nombre.
138
+ - **Herramientas**: `nex inspect` añade los nodos `RecordDecl`, `FieldDecl`, `RecordConstruction`,
139
+ `FieldInitializer` y `FieldAccess`, la sentencia `FieldAssign` y el campo `records` al final de
140
+ `Program`; `type` puede ser el nombre de un registro. `nex context` añade el campo `records`
141
+ (con `name`, `fields`, `valid`, `line` y `reason`) y el valor `records` en `features`. `types`
142
+ sigue siendo solo de tipos refinados.
143
+ - **Diagnósticos**: `E2023` (Invalid record construction, `type`), `E2024` (Invalid field access,
144
+ `type`) y `E2025` (Cannot reassign a field, `semantic`).
145
+ - **Ejemplo** [`examples/records.nex`](examples/records.nex), con su salida registrada en las
146
+ pruebas.
147
+
148
+ ### Cambiado
149
+
150
+ - Solo en programas que ya eran inválidos: `p.x`, donde `p` es una variable que no es un tipo,
151
+ pasa de ser `E1101` («Expected '(' to call 'p.x'») a ser un acceso a un campo. La forma de
152
+ llamada cualificada no cambia: `Text.length(s)` y `name.length()` se siguen diagnosticando igual,
153
+ y `Point.x` sigue siendo una llamada cualificada, no la lectura de un campo.
154
+ - La versión del repositorio pasa a `0.8.0` y la especificación a `0.8`. Los esquemas JSON no
155
+ cambian de versión: los campos y nodos nuevos son aditivos.
156
+
157
+ ### Documentación
158
+
159
+ - La [especificación](docs/language-spec.md) pasa a la versión 0.8 y documenta los registros (4.6),
160
+ su gramática, su igualdad nominal, su impresión, la tabla de diagnósticos y las limitaciones.
161
+ - Actualizados `types.md`, `limitations.md`, `roadmap.md`, `architecture.md`, `index.md`,
162
+ `diagnostics.md`, `inspect-schema.md`, `json-protocol.md` y `README.md`.
163
+ - La [decisión 0014](docs/decisions/0014-registros-nominales-inmutables.md) pasa a aceptada, con su
164
+ implementación. La [0012](docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md)
165
+ no se reescribe y sigue siendo una propuesta para lo no implementado.
166
+
167
+ ### Limitaciones
168
+
169
+ - Un registro no tiene métodos, ni funciones propias, ni refinamientos, ni recursión, ni parámetros
170
+ de tipo. Un tipo refinado no puede construirse sobre un registro (`E2016`).
171
+ - Un registro no puede contenerse a sí mismo, ni directamente ni a través de otro registro ni de
172
+ una lista (`E2016`).
173
+ - No hay `Option`, `Result`, enums, `match`, genéricos de usuario ni entrada de datos.
174
+
175
+ ## 0.7.0 — Listas y `for` sobre listas
176
+
177
+ Estado del repositorio, no una versión publicada: no hay tag ni paquete en ningún repositorio. Esta
178
+ versión implementa la [decisión 0013](docs/decisions/0013-listas-y-for.md), que acepta, en el alcance
179
+ realmente implementado, las partes D01 (modelo de tipos), D08 (listas) y D09 (`for`) de la propuesta
180
+ [0012](docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md). El resto de la
181
+ 0012 sigue siendo una propuesta y no forma parte de esta versión.
182
+
183
+ ### Añadido
184
+
185
+ - **Listas inmutables `List[T]`**: literales `[1, 2, 3]` y `[]`, inferencia por el primer elemento,
186
+ homogeneidad, listas anidadas, igualdad estructural e impresión `[1, 2, 3]`. Una lista vacía
187
+ necesita un tipo esperado (`E2011`). Los argumentos de `List` son invariantes y `Void` no puede
188
+ ser elemento.
189
+ - **Funciones de `List`**: `List.length`, `List.get`, `List.append`, `List.concat` y
190
+ `List.contains`. `List.get` es parcial y declara `requires 0 <= i and i < List.length(xs)`; un
191
+ índice fuera de rango detiene el programa con `E4001`. `append` y `concat` construyen listas nuevas.
192
+ - **`for` sobre listas**: `for x in xs { ... }`, con iterador inmutable y de tipo `T`, un entorno
193
+ nuevo por vuelta, `invariant` comprobados antes de cada vuelta y una vez después de la última, y
194
+ sin `decreases` (`E2022`). El iterable se evalúa una vez y debe ser una `List[T]`. Siguen sin
195
+ existir `break` y `continue`.
196
+ - **Modelo de tipos interno** (`types.py` y `type_adapter.py`): representación estructurada de
197
+ primitivos, `List[T]`, tipos refinados y variables de tipo; identidad, compatibilidad, sustitución
198
+ y unificación. La forma canónica de los tipos sigue siendo el texto; la migración del comprobador
199
+ es parcial y las reglas no migradas conservan sus cadenas.
200
+ - **Herramientas**: `nex inspect` añade `arguments` a `TypeRef`, el nodo `ListLiteral` y el nodo
201
+ `For`; `type` puede ser `List[...]`. `nex context` distingue `while` y `for` en `loops.kind`.
202
+ `language_info()` incluye `List` en `types`, para que un editor lo coloree como los demás tipos.
203
+ - **Ejemplo** [`examples/lists.nex`](examples/lists.nex), con su salida registrada en las pruebas.
204
+
205
+ ### Cambiado
206
+
207
+ - Solo en programas que ya eran inválidos: `for` e `in` pasan de palabras reservadas a palabras
208
+ clave, y `[` y `]` pasan a ser símbolos. `List` y los nombres de sus funciones no se reservan: las
209
+ funciones se llaman cualificadas (`List.length`).
210
+ - La versión del repositorio pasa a `0.7.0`. Los esquemas JSON no cambian de versión: los campos y
211
+ nodos nuevos son aditivos.
212
+
213
+ ### Documentación
214
+
215
+ - La [especificación](docs/language-spec.md) pasa a la versión 0.7 y documenta las listas (4.5), las
216
+ funciones de `List` (8.2), `for` (9.6), la compatibilidad, las conversiones, los diagnósticos y las
217
+ limitaciones.
218
+ - Nuevo [ADR 0013](docs/decisions/0013-listas-y-for.md). La [0012](docs/decisions/0012-modelo-de-tipos-patrones-iteracion-y-verificabilidad.md)
219
+ no se reescribe y sigue siendo una propuesta para lo no implementado.
220
+ - Actualizados `types.md`, `limitations.md`, `roadmap.md`, `architecture.md`, `index.md`,
221
+ `diagnostics.md`, `inspect-schema.md` y `README.md`.
222
+
223
+ ### Limitaciones
224
+
225
+ - No hay indexación sintáctica (`xs[i]`), mutación de elementos, `List.set`, `map`, `filter`,
226
+ ordenación ni una variante total de `List.get`.
227
+ - Todos los elementos de una lista tienen el mismo tipo; `[]` y `for x in []` necesitan un tipo
228
+ esperado. No se puede refinar una lista (`E2016`).
229
+ - Un `for` no cuenta como una ruta que siempre devuelve: si es la última sentencia de una función
230
+ que devuelve un valor, hace falta un `return` después.
231
+ - No hay `for` sobre rangos o `Text`, ni genéricos de usuario.
232
+
233
+ ## Sin publicar — servidor y web
234
+
235
+ Cambios del servidor web (`servidor/`) y de la web (`web/`) posteriores al tag `v0.6.0`, todavía sin
236
+ versión. No forman parte de la 0.7.0 y no cambian el intérprete: el lenguaje y el paquete
237
+ `nexora-lang` de esta versión están documentados arriba.
238
+
239
+ ### Añadido
240
+
241
+ - **Antispam en el registro y el inicio de sesión** (`servidor/src/captcha.js`): Google reCAPTCHA v3,
242
+ un campo trampa y un límite estricto por cliente (5 registros o 15 inicios de sesión por hora) para
243
+ toda petición sin un token que Google confirme. reCAPTCHA solo se carga en el navegador si la
244
+ persona lo acepta en el panel de cookies; sin él se puede entrar igual, con ese límite. La clave
245
+ secreta va en `RECAPTCHA_SECRET_KEY` y la del sitio en `web/js/consent-config.js`; al arrancar, el
246
+ servidor avisa si solo está una de las dos.
247
+ - **Freno del inicio de sesión por cuenta** (`servidor/src/login-throttle.js`): tras 5 fallos desde
248
+ un cliente, ese cliente espera cada vez más, hasta 15 minutos; tras 20 fallos contra una cuenta
249
+ desde cualquier cliente, cada intento espera, como mucho un minuto. Responde igual para un email
250
+ con cuenta y uno sin ella, y reserva cada intento antes de consultar la base de datos.
251
+ - **Límites por cliente con IPv6** (`servidor/src/rate-limits.js`): todos los límites cuentan una
252
+ dirección IPv6 por su bloque /56, y además cada red /48 con cuatro veces el cupo. Las cabeceras
253
+ `RateLimit-*` describen la cuenta que antes se agota, y cada rechazo lleva `Retry-After`.
254
+ - **Borrado de las sesiones caducadas** al arrancar y cada hora, por tandas, sin que dos pasadas se
255
+ solapen (`servidor/src/session-cleanup.js`).
256
+ - **Cabeceras de seguridad en todas las respuestas** (`servidor/src/security-headers.js`): la
257
+ política de contenido (CSP) sin código en línea que tenían el playground y la guía pasa a todas las
258
+ páginas, más `X-Frame-Options`, `Cross-Origin-Opener-Policy`, `Permissions-Policy`,
259
+ `Referrer-Policy` y, en producción y solo por HTTPS, HSTS. La API tiene una política que no carga
260
+ nada.
261
+ - `LISTEN_HOST`: la dirección en la que escucha el servidor. Con nginx en el mismo equipo,
262
+ `LISTEN_HOST=127.0.0.1` y `TRUST_PROXY=loopback`; al arrancar, el servidor avisa si no casan.
263
+ - Web: las tipografías se sirven desde el propio sitio (JetBrains Mono, con su licencia OFL) en vez
264
+ de Google Fonts, y todo el sitio usa JetBrains Mono; un gestor de consentimiento de cookies con
265
+ Google Consent Mode v2; las páginas legales, todavía en borrador (ver Limitaciones); y una
266
+ documentación web ampliada: qué es NEXORA, cómo se prueba y los cambios recientes.
267
+ - `GET /api/playground/status` dice si el playground está activado (`{"enabled": true}` o `false`).
268
+ Responde también con el playground desactivado y no arranca el worker. La página lo pregunta
269
+ primero: desactivado, no pide `/info` ni `/examples`.
270
+ - Las páginas legales llevan `<meta name="robots" content="noindex, nofollow">` mientras sean un
271
+ borrador, y una prueba lo exige. Solo pide a los buscadores que no las indexen: no impide abrirlas
272
+ a quien tenga el enlace.
273
+ - `GET /api/health`: dice si el proceso está en marcha y responde peticiones
274
+ (`{"status":"ok","checks":{"http":"ok"}}`), y nada más: no comprueba MySQL, Google, GitHub,
275
+ reCAPTCHA ni el playground, ni revela la versión o la configuración. Durante el apagado responde 503.
276
+ - **Apagado ordenado** (`servidor/src/lifecycle.js`): con `SIGTERM` o `SIGINT`, el servidor deja de
277
+ aceptar conexiones y responde con `Connection: close`, para que una conexión persistente no lo
278
+ retenga; espera hasta 10 s a las peticiones en curso y a la tanda de borrado de sesiones que esté
279
+ en MySQL (no envía ninguna más); cierra el pool de MySQL (otros 10 s como mucho) y sale con 0, o
280
+ con 1 si tuvo que cortar algo. Si durante el apagado llega la otra
281
+ señal, no se repite; si llega otra vez la misma, Node termina el proceso en el acto, sin esperar.
282
+ El límite de cada espera mantiene vivo el proceso hasta que vence: si lo único pendiente no tiene
283
+ ningún recurso detrás (un cierre del pool que no termina), el proceso ya no sale con 0 antes de
284
+ tiempo sin registrarlo, sino que espera el límite, lo registra y sale con 1. Probado con pruebas
285
+ automatizadas en Windows (Node 20.6.1, 22.23.3 y 24.21.0), sin señales reales: con systemd, sin
286
+ probar.
287
+ - **Pruebas de integración con MySQL, preparadas y nunca ejecutadas** (`servidor/test/integration/`).
288
+ `npm run test:integration` ejecutaría las consultas reales de la aplicación contra una base de pruebas
289
+ aislada (`nexora_test`), con una cuenta para las migraciones y otra para la aplicación, que un
290
+ administrador tendría que crear a mano con `setup-admin.sql` (una propuesta pendiente de revisión).
291
+ No forman parte de `npm test` ni de la integración continua. Antes de cargar nada, unas guardas
292
+ rechazan cualquier otro entorno (variables `DB_*`, otro servidor, el playground activable o permisos
293
+ distintos de los previstos), y un bloqueo con nombre (`GET_LOCK`) impide dos ejecuciones a la vez.
294
+ Las guardas solo admiten Node 22 desde 22.22.3 y Node 24 desde 24.14.0 (`HOOK_MINIMUMS` en
295
+ `servidor/test/integration/guard.js`): son los mínimos medidos en Windows para el gancho de módulos de
296
+ `preflight.js`, no los de `engines` en `servidor/package.json` (`^22.23.2 || ^24.18.1`), y una prueba
297
+ de `npm test` comprueba que todo lo que admite `engines` está dentro. Las demás ramas (23, 25, 26 y
298
+ posteriores) y las versiones que no son exactamente X.Y.Z se rechazan hasta medirlas. En las
299
+ versiones medidas por debajo de esos mínimos, el gancho bloqueó mysql2 también en el orden legítimo;
300
+ la causa no está confirmada. En un Node que las guardas no admiten, `npm test` omite con su motivo las 14 pruebas que lanzan
301
+ `preflight.js`: así ocurrió en la ejecución experimental de Node 26 de la integración continua
302
+ del 21 de octubre de 2026. **Nada de esto se ha ejecutado contra un MySQL
303
+ real**: ni el SQL de administración, ni las migraciones con permisos reducidos, ni el bloqueo.
304
+
305
+ ### Cambios incompatibles
306
+
307
+ - **El servidor requiere Node.js 22.23.2 o posterior en la rama 22, o 24.18.1 o posterior en la rama
308
+ 24** (`engines` en `servidor/package.json`: `^22.23.2 || ^24.18.1`; antes, 20.6 o posterior). Node 20
309
+ no tiene soporte desde el 30 de abril de 2026, y las mínimas son las últimas versiones de seguridad
310
+ de cada rama en octubre de 2026. Las ramas 23, 25 y 26 quedan fuera. npm solo avisa (`EBADENGINE`)
311
+ con otra versión, no impide instalar. Dentro del rango se han probado en local, en Windows, 22.23.3
312
+ y 24.21.0; los mínimos exactos no. La integración continua del servidor pasa a probar 22.23.2,
313
+ 24.18.1, la última de las ramas 22 y 24 y, sin bloquear, Node 26; se ejecutó por primera vez el
314
+ 21 de octubre de 2026.
315
+ - **Con `NODE_ENV=production`, `PLAYGROUND_ENABLED=true` exige la orden de contenedor** en
316
+ `PLAYGROUND_WORKER_COMMAND`. Sin ella (o con una orden de proceso), el servidor no arranca: el modo
317
+ de proceso ejecuta el código de los visitantes con los permisos del servidor. Fuera de producción,
318
+ el modo de proceso sigue disponible para probar en un equipo propio. Configurar la orden de
319
+ contenedor no demuestra que el aislamiento funcione: sigue sin comprobarse con Docker real.
320
+
321
+ - **`NODE_ENV` solo admite `development`, `test` o `production`, en minúsculas.** Antes cualquier
322
+ valor distinto de `production` (`Production`, `prod`, vacío) dejaba el servidor en desarrollo, sin
323
+ cookies solo por HTTPS. Ahora esos valores impiden arrancar. Sin definir sigue funcionando como
324
+ desarrollo, y el servidor lo avisa al arrancar.
325
+ - **`TRUST_PROXY` solo admite `0`, `loopback` o un entero de 1 a 10 sin ceros a la izquierda.**
326
+ Antes se leía con `Number()`: `true` o `nginx` daban `NaN` sin ningún aviso. Ahora cualquier otro
327
+ valor impide arrancar. Con nginx en el mismo equipo, se recomienda `loopback` en vez de `1`.
328
+ - `LISTEN_HOST` solo admite una dirección IP o quedar vacía; un nombre, también `localhost`, impide
329
+ arrancar.
330
+ - Con `NODE_ENV=production` y `RECAPTCHA_SECRET_KEY`, falta `RECAPTCHA_HOSTNAMES` impide arrancar.
331
+ `RECAPTCHA_MIN_SCORE` fuera de 0 a 1, o un `RECAPTCHA_HOSTNAMES` que no sea una lista de dominios,
332
+ también.
333
+ - **En producción sin `RECAPTCHA_SECRET_KEY`, todos los registros e inicios de sesión pasan por el
334
+ límite estricto** (5 registros o 15 inicios de sesión por cliente y hora). En desarrollo no cambia
335
+ nada.
336
+ - La API de cuentas tiene respuestas nuevas: `429` con `code: "login_throttled"` y `Retry-After`
337
+ durante el freno del inicio de sesión; `400` con `captcha_invalid` o `captcha_expired`; y `429`
338
+ con `captcha_required` en el límite estricto. Un campo `website` con contenido se rechaza con `400`.
339
+
340
+ - **El playground está desactivado salvo que se active de forma explícita.** Antes se activaba
341
+ siempre que `PLAYGROUND_ENABLED` no fuera exactamente `false`: sin definir, vacía o con una errata
342
+ (`False`, `0`), quedaba abierto. Ahora solo `PLAYGROUND_ENABLED=true` lo activa; sin definir,
343
+ vacía o `false`, la API responde 503 (`playground_disabled`) y no arranca ningún worker. Una
344
+ instalación local que quiera usar el playground tiene que añadir `PLAYGROUND_ENABLED=true` a su
345
+ `servidor/.env`.
346
+ - Cualquier otro valor de `PLAYGROUND_ENABLED` (`True`, `1`, `yes`…) impide arrancar el servidor,
347
+ con un mensaje que pide `"true"` o `"false"`.
348
+ - **La única orden de contenedor que admite `PLAYGROUND_WORKER_COMMAND` es la documentada.** Tiene
349
+ que empezar por `"docker","run"`, llevar exactamente una vez cada opción de aislamiento y de
350
+ límites de `servidor/.env.example` con su valor exacto, entre ellas `"--init"`,
351
+ `"--pull","never"` y `"--name","{container}"`, y terminar en la imagen. Cualquier otra orden de
352
+ contenedor impide arrancar el servidor, aunque el playground esté desactivado, con un mensaje que
353
+ nombra lo que falla. Eso incluye una opción que falte, se repita, tenga otro valor o no esté en la
354
+ lista, como los alias `-m`, `-u`, `--net` o `--interactive`; la forma `--opción=valor`; algo
355
+ después de la imagen; una ruta al ejecutable, `docker.exe`, `sudo`, Podman,
356
+ `docker container run` u opciones globales de Docker. Lo que va después de la imagen no lo aplica
357
+ Docker, sino que lo recibe el programa del contenedor. Con la comprobación anterior, un
358
+ `"--name","{container}"` escrito detrás de la imagen se aceptaba (comprobado con el validador
359
+ anterior) y, según el código, el servidor podía dar por eliminado un contenedor que seguía vivo;
360
+ no se ha observado con Docker real. Para actualizar una configuración existente, copia la orden
361
+ de `servidor/.env.example`. Cambiar un límite exige cambiar el servidor
362
+ (`servidor/src/playground/config.js`), no solo `servidor/.env`.
363
+ - **Solo se ejecuta la imagen local `nexora-worker`, y Docker no la descarga nunca.** La imagen
364
+ tiene que ser `nexora-worker` o `nexora-worker:<etiqueta>`: no se admite otro nombre, una ruta, un
365
+ registro ni un digest. `"--pull","never"` hace que, si la imagen no está en el equipo, la
366
+ ejecución falle en vez de descargarla. Hay que construir la imagen en el servidor antes de
367
+ activar el playground y en cada actualización (ver la
368
+ [guía del playground](docs/playground.md#3-imagen-del-worker)). `--pull` necesita Docker 20.10 o
369
+ posterior.
370
+ - Las órdenes del modo de proceso siguen sin comprobarse, salvo las que contienen `{container}` o
371
+ un ejecutable llamado `docker` o `podman`: esas se tratan como órdenes de contenedor y se
372
+ rechazan si no son la documentada.
373
+
374
+ ### Corregido
375
+
376
+ - **Un contenedor que agota el tiempo ya no sigue ejecutándose con su hueco liberado.** Antes, el
377
+ servidor mataba el cliente `docker run`, que no detiene el contenedor, y liberaba el hueco en ese
378
+ momento. Según el código, eso permitía que hubiera más contenedores vivos que
379
+ `PLAYGROUND_MAX_CONCURRENT`; no se ha observado con Docker real, porque el modo contenedor
380
+ todavía no se ha probado. Ahora cada
381
+ contenedor tiene un nombre generado por el servidor; si no termina bien, se elimina por ese
382
+ nombre y el hueco solo se libera cuando se confirma que ya no existe. Los intentos de eliminarlo
383
+ están limitados; si ninguno confirma, el hueco queda ocupado.
384
+ - **El botón Run del playground vuelve a verse en móvil.** A 390 px quedaba cortado y a 320 y 360 px
385
+ no se veía; solo quedaba el atajo Ctrl+Enter. Por debajo de 440 px los cuatro botones van ahora en
386
+ dos filas.
387
+ - **Con el playground desactivado, la página ya no parece lista para ejecutar.** Antes mostraba
388
+ «Real interpreter» con un punto verde y Run y Check activos, y cada carga recibía dos respuestas
389
+ 503. Ahora dice «Disabled», Run y Check están desactivados (también sus atajos), la ayuda y el pie
390
+ no dicen que el código se ejecuta, y no envía ninguna petición de ejecución. Mientras espera la
391
+ respuesta del servidor dice «Connecting…». Si el servidor no confirma si está activado (no
392
+ responde o responde algo que la página no entiende), dice «Status unknown» y tampoco deja ejecutar:
393
+ Run y Check solo se activan cuando el servidor lo confirma. «Real interpreter» en verde solo
394
+ aparece si además el intérprete respondió; si no, «Unavailable». Activado, se comporta como antes.
395
+ - El formulario de registro cabe entero, botón incluido, en una pantalla de 1366×768. El inicio de
396
+ sesión ya cabía y no cambia.
397
+ - **El registro es atómico.** Antes se creaba la cuenta y después, aparte, su primera sesión: si la
398
+ sesión no se podía guardar, la cuenta quedaba creada, la persona recibía un error y, al volver a
399
+ intentarlo, un 409. Ahora la cuenta y la sesión se guardan en una sola transacción, la cookie solo
400
+ se envía después de confirmarla, y el resumen de la contraseña se calcula antes de abrirla, para no
401
+ ocupar una conexión del pool durante scrypt. Si además falla el `ROLLBACK`, cuenta el error
402
+ original (un email duplicado sigue respondiendo 409) y esa conexión no vuelve al pool, porque podría
403
+ haber quedado con la transacción abierta: se destruye. El fallo del `ROLLBACK` queda en el registro
404
+ con su tipo y su código (`auth: sign-up rollback failed (Error ER_ERROR_DURING_ROLLBACK)`), sin el
405
+ mensaje ni ningún dato de la petición. Probado con una base de datos simulada; contra MySQL real, la
406
+ prueba está preparada y sin ejecutar.
407
+ - El inicio de sesión con Google o GitHub sigue la misma regla en su transacción: si falla el
408
+ `ROLLBACK`, cuenta el error original (por ejemplo, `account_exists` sigue siendo `account_exists`), la
409
+ conexión se destruye en vez de volver al pool (una sola vez, nunca devuelta y destruida) y queda una
410
+ línea `oauth: <proveedor>: rollback failed (…)` sin datos. Antes, un `ROLLBACK` fallido sustituía al
411
+ error original y la conexión volvía al pool. Probado con una base de datos simulada.
412
+ - **El registro del servidor ya no copia los errores enteros.** Un error interno de la aplicación o
413
+ del inicio de sesión con Google o GitHub escribía el objeto completo, que en un error de MySQL
414
+ incluye la consulta y el mensaje del servidor (con el email, por ejemplo). Ahora solo se escribe el
415
+ tipo y un código conocido (`Error ER_DUP_ENTRY`, `ProviderError token_http_500`), nunca el mensaje,
416
+ la pila, la consulta, el token, el código de autorización ni el secreto. La ruta se escribe por su
417
+ plantilla (`/api/auth/:provider/callback`), nunca por el camino pedido ni su cadena de consulta; sin
418
+ una ruta que coincida, `(no route)`. El borrado de sesiones caducadas sigue la misma regla: antes
419
+ escribía el mensaje del error cuando no tenía código.
420
+ - Google: una cuenta solo se crea o se enlaza si `email_verified` es exactamente `true`; antes
421
+ bastaba cualquier valor que JavaScript tome por verdadero. Un perfil sin identificador, o una lista
422
+ de emails de GitHub que no es una lista, son ahora un fallo del proveedor con su propio código.
423
+ - Un resumen de contraseña guardado con campos de más (`…$extra`) ya no se acepta como válido: el
424
+ formato tiene que ser exactamente el de `hashPassword`.
425
+
426
+ ### Documentación
427
+
428
+ - `servidor/.env.example` deja el playground en `false`, y la [guía del playground](docs/playground.md)
429
+ lo mantiene desactivado en un servidor público hasta completar la
430
+ [comprobación del aislamiento](docs/playground.md#comprobar-el-aislamiento). Las comprobaciones
431
+ que necesitan el playground activo se hacen en un entorno controlado, sin exposición pública.
432
+ - Linux comprobado ([Pruebas](docs/testing.md#linux)): en Ubuntu 24.04 sobre WSL 2 pasan las
433
+ pruebas automáticas de `nexora/` con Python 3.11, 3.12 y 3.13 (también las cuatro de POSIX, que
434
+ nunca se habían ejecutado) y las de `npm/` y `servidor/`, y los paquetes de pip y npm se instalan
435
+ y funcionan. No hizo falta cambiar código. Docker y el aislamiento del playground siguen sin
436
+ comprobar.
437
+ - Despliegue ([guía del playground](docs/playground.md#despliegue-en-un-servidor-propio)): nginx con
438
+ redirección de HTTP a HTTPS, `limit_req`, `server_tokens off` y la sintaxis `http2 on` de las
439
+ versiones actuales; `TimeoutStopSec` en systemd; la ruta de salud; y copias de seguridad y
440
+ restauración de MySQL. **Nada de ello se ha probado** con nginx, systemd ni MySQL reales: la guía
441
+ lo dice en cada apartado.
442
+
443
+ ### Limitaciones
444
+
445
+ - El aislamiento en contenedor sigue **sin comprobar**: este cambio evita activar el playground por
446
+ error, pero no lo hace más seguro una vez activado.
447
+ - Nada de esto se ha probado con Docker real. Las pruebas de la orden solo analizan la lista de
448
+ argumentos, sin ejecutar ningún Docker; las del ciclo de vida (eliminación de contenedores, hueco
449
+ ocupado, imagen ausente) usan un Docker falso. Si el servidor se cae, nadie elimina los
450
+ contenedores que tuviera en marcha.
451
+ - Qué es una orden de contenedor se decide solo por los nombres documentados. Un script
452
+ envoltorio, otra herramienta (`nerdctl`…) o un ejecutable de Docker con otro nombre quedan fuera
453
+ de la detección: no son una forma admitida de ejecutar contenedores, y el servidor no validaría
454
+ su orden ni eliminaría sus contenedores.
455
+ - Validar la orden no garantiza la configuración efectiva del demonio Docker ni de sus runtimes.
456
+ - La etiqueta local (`nexora-worker` o `nexora-worker:<etiqueta>`) es mutable: quien tenga acceso
457
+ al demonio Docker puede volver a etiquetar otra imagen con ese nombre, y el servidor la
458
+ ejecutaría sin notarlo. Que se ejecute la imagen de Nexora depende del control de acceso al
459
+ equipo y al demonio Docker.
460
+ - Docker en modo *rootless* no está admitido: la orden no permite `-H` ni `--context`, y el
461
+ servidor solo pasa `PATH` y `SystemRoot` a las órdenes que lanza.
462
+ - El antispam, el freno del inicio de sesión, el borrado de sesiones y las cabeceras se han probado
463
+ con la base de datos, Google reCAPTCHA y el navegador simulados: no contra un MySQL real, con claves
464
+ reales de reCAPTCHA, en un navegador ni detrás de nginx. Las pruebas automatizadas del servidor,
465
+ con esos mismos servicios simulados, dan 811 superadas y 0 fallos con Node 24.21.0 (una ejecución,
466
+ en Windows 11, con las guardas de las pruebas de integración limitadas a las ramas medidas). Antes de
467
+ ese cambio dieron 810 superadas y 0 fallos con Node 24.21.0 (en cada una de
468
+ tres ejecuciones), 24.14.0, 22.23.3 y 22.22.3, y 796 superadas, 14 omitidas (las que necesitan Node
469
+ 22.22.3 o posterior en la rama 22, o 24.14.0 o posterior) y 0 fallos con Node 20.6.1 y 22.15.0 (una
470
+ ejecución con cada una de esas versiones), en Windows 11. Con los mínimos de `engines` (22.23.2 y
471
+ 24.18.1) no se han ejecutado. En Ubuntu 24.04 en WSL 2 se ejecutaron antes de las últimas correcciones de la web
472
+ (567 superadas, 0 fallos, en cada una de tres ejecuciones) ([Pruebas](docs/testing.md)).
473
+ - Los contadores de los límites y del freno del inicio de sesión están en memoria: se pierden al
474
+ reiniciar y no se comparten entre varias instancias del servidor.
475
+ - En el inicio de sesión con Google o GitHub, la sesión se crea fuera de la transacción de la cuenta y
476
+ su enlace: si falla, la cuenta queda enlazada sin sesión hasta el siguiente intento. Durante el
477
+ apagado, un segundo `SIGTERM` o `SIGINT` termina el proceso sin completar la limpieza, y el 503 de
478
+ `/api/health` solo llega por conexiones ya abiertas. Las pruebas automatizadas, en Windows y con la
479
+ base de datos simulada, cubren el fallo de la sesión de OAuth y el reintento, el 503 durante el
480
+ apagado y el rechazo de las conexiones nuevas. No cubren la segunda señal del mismo tipo ni dos
481
+ primeros inicios simultáneos con la misma identidad, que siguen sin probar. Quedan pendientes las
482
+ pruebas contra un MySQL real, con señales reales del sistema operativo, con systemd y detrás de
483
+ nginx ([Tareas pendientes y riesgos](docs/playground.md#tareas-pendientes-y-riesgos)).
484
+ - Las páginas legales son un borrador sin los datos del titular ni revisión jurídica: no deben
485
+ publicarse así. Riesgos y decisiones pendientes (Analytics y su CSP, el `409` que revela si un email
486
+ tiene cuenta, la cancelación de consultas de MySQL):
487
+ [Tareas pendientes y riesgos](docs/playground.md#tareas-pendientes-y-riesgos).
488
+
489
+ ## 0.6.0 — Operaciones sobre textos que declaran sus requisitos
490
+
491
+ Un programa puede medir un texto, cortarlo, buscar en él, ordenarlo y leer los números escritos en
492
+ él. Las funciones nuevas pertenecen a un tipo y se llaman con su nombre (`Text.length`,
493
+ `Int.parse`…), y las que pueden no tener respuesta declaran cuándo la tienen con un `requires`
494
+ escrito en NEXORA, igual que una función del programa. Implementa la
495
+ [decisión 0011](docs/decisions/0011-operaciones-sobre-textos.md). Ningún programa válido en la 0.5.1
496
+ deja de serlo ni cambia de resultado, y no se reserva ningún nombre. Para un programa válido solo
497
+ cambian los documentos de las herramientas, que ganan campos y entradas sin cambiar de versión:
498
+ `"qualifier": null` en los nodos `Call` de `nex inspect`, y las funciones nuevas en `nex symbols`.
499
+
500
+ ### Añadido
501
+
502
+ - **Funciones de un tipo**, llamadas como `Tipo.función(…)`:
503
+ - `Text.length`, `Text.slice`, `Text.contains`, `Text.starts_with` y `Text.ends_with`. Trabajan
504
+ con caracteres (puntos de código Unicode), sin normalizar y distinguiendo mayúsculas.
505
+ - `Int.can_parse`, `Int.parse`, `Float.can_parse` y `Float.parse`. Leen una única forma de
506
+ número, la misma en cualquier máquina: `Int.parse` un entero dentro del rango de `Int`
507
+ (`"-42"`), y `Float.parse` un número con parte decimal o exponente (`"2.5"`, `"2.5e3"`, pero no
508
+ `"42"`). No admiten espacios, `+` delante, separadores ni otras bases.
509
+ - **Las funciones del lenguaje tienen `requires`.** `Text.slice`, `Int.parse` y `Float.parse`
510
+ declaran sus condiciones en NEXORA, con su explicación, y una llamada que las incumple se detiene
511
+ con el mismo `E4001` que una función del programa (código de salida `5`). En `Int.parse` y
512
+ `Float.parse`, el diagnóstico añade una sugerencia según lo que falló: comprobar antes el texto con
513
+ `Int.can_parse` o `Float.can_parse`, o el rango de `Int` si el número no cabe. Un programa puede
514
+ usar las mismas funciones en sus propios `requires` (`requires Int.can_parse(written)`) y en sus
515
+ tipos refinados (`type Code = Text where Text.length(value) == 3`).
516
+ - **Orden entre textos:** `<`, `<=`, `>` y `>=` entre dos `Text` (también de tipos refinados con
517
+ base `Text`). Se comparan carácter a carácter por punto de código, y un texto va antes que
518
+ cualquier otro que empiece por él (`"ab" < "abc"`, `"" < "a"`). No depende del idioma ni de la
519
+ configuración del sistema (`"Z" < "a"`), y compara caracteres, no números (`"10" < "9"`).
520
+ - Diagnósticos para la nueva sintaxis, sin códigos nuevos: `E1101` cuando la forma está mal
521
+ (`Text.length` sin paréntesis, espacios alrededor del `.`, más de un punto) y `E2008` cuando el
522
+ nombre no existe, con sugerencias como `Did you mean 'Text.length'?` para `length(t)`.
523
+ - `nex inspect`: campo `qualifier` en cada nodo `Call` (`null` en las llamadas sin cualificar),
524
+ sin cambiar la versión del esquema (`nexora.inspect/2`). `nex symbols` y las capacidades
525
+ (`language.builtins`): nombres cualificados y campo `contracts` con los requisitos de cada función
526
+ integrada. `nex context`: nombres cualificados en `builtins_called` y valor `text_operations` en
527
+ `features`.
528
+ - Ejemplos `texts.nex` y `failing/parse_number.nex`.
529
+
530
+ ### Cambiado
531
+
532
+ Solo en programas que ya eran inválidos:
533
+
534
+ - Un `.` fuera de un número, un texto o un comentario pasa a ser un token. Donde antes había `E1001`
535
+ (`x.y`), ahora hay `E1101` o `E2008`.
536
+ - `E2011` mantiene su código, su título y su categoría, pero su sugerencia, cuando se compara un
537
+ `Text` con un número, propone convertir uno de los lados con `to_text(...)` o `Int.parse(...)`.
538
+ Ordenar dos `Text` ya no es un error.
539
+
540
+ ### Corregido
541
+
542
+ - **El paquete de código fuente no podía ejecutar su propia batería de pruebas.** `MANIFEST.in` no
543
+ incluía los esquemas JSON de `tests/golden/schemas/`, y las pruebas que los leen fallaban en una
544
+ copia generada con `python -m build`. El paquete instalable (`.whl`) no estaba afectado.
545
+
546
+ ### Documentación
547
+
548
+ - Especificación 0.6: sección 8.1 (funciones de un tipo y orden entre textos).
549
+ - Guías: requisitos de las funciones del lenguaje en
550
+ [contratos](docs/contracts.md#las-funciones-del-lenguaje-también-tienen-requires), y límites
551
+ actualizados en [limitaciones](docs/limitations.md).
552
+ - El ejemplo de bucles de la guía web y de la especificación suma hasta 10 000 en lugar de 100 000:
553
+ así se ejecuta con holgura dentro del límite de tiempo del playground.
554
+
555
+ ### Limitaciones
556
+
557
+ - Un carácter es un punto de código, no lo que una persona ve como una letra: una `e` con el acento
558
+ combinado tiene longitud 2, y dos textos que se ven iguales pueden ser distintos para `==`.
559
+ - El orden entre textos no es alfabético en ningún idioma.
560
+ - Los requisitos de las funciones integradas se comprueban al ejecutar; `nex check` no avisa aunque
561
+ los argumentos sean constantes.
562
+ - `Float.parse` redondea al `Float` más cercano: el valor que devuelve puede no ser exactamente el
563
+ número escrito (`"0.1"`).
564
+ - No hay `trim`, mayúsculas, `split`, `replace`, interpolación ni búsqueda de posiciones.
565
+
566
+ ## 0.5.1 — Revisión de la memoria agotada
567
+
568
+ Resultado de auditar la [decisión 0009](docs/decisions/0009-memoria-agotada.md) después de
569
+ implementarla. Su garantía se cumple: `E3007` se sigue construyendo sin retener los valores del
570
+ programa que falló, y cada ejemplo de `nex test` se evalúa aislado. La auditoría encontró dos
571
+ defectos alrededor de ella, corregidos aquí. Ningún programa cambia de resultado ni de código de
572
+ salida.
573
+
574
+ ### Corregido
575
+
576
+ - **`E3007` y el `E3004` por falta de pila no decían en qué bucle ocurrieron.** La
577
+ [decisión 0010](docs/decisions/0010-var-y-while.md) (sección 7) da a todo error de ejecución
578
+ dentro de un bucle una nota por bucle (`In turn 3 of the loop at line 6.`). Estos dos se
579
+ construyen cuando el error ya ha salido de los bucles, y llegaban sin ellas. Ahora llevan las
580
+ mismas notas que cualquier otro error en el mismo punto, del bucle más interior al exterior y con
581
+ el mismo máximo de seis.
582
+ - **`nex test` en texto podía dejar un resumen sin terminar.** El informe se escribía a medida que
583
+ se preparaba: si fallaba después del resumen (un error interno, la memoria agotada), ese resumen
584
+ quedaba en la salida estándar igual que en un informe completo, con el código `70`. Ahora el
585
+ informe se prepara entero y se escribe al final, como el documento JSON: si algo falla, la salida
586
+ estándar queda vacía. Los informes normales no cambian.
587
+
588
+ ### Limitaciones
589
+
590
+ - La prueba que agota memoria real (la del worker, con `RLIMIT_AS`) solo puede ejecutarse en Linux
591
+ y macOS, y no se ha ejecutado todavía: en Windows se omite y la integración continua no ha
592
+ funcionado nunca. Las demás pruebas de `E3007` provocan el `MemoryError` a propósito.
593
+
594
+ ## 0.5.0 — `var` y bucles que dicen por qué terminan
595
+
596
+ La repetición deja de depender de la recursividad y del límite de 1000 llamadas. Y lo hace a la
597
+ manera de NEXORA: cada bucle declara por qué termina (`decreases`) y qué mantiene (`invariant`), y
598
+ el intérprete lo comprueba en cada vuelta. Así ningún bucle se queda repitiendo para siempre: o
599
+ termina, o se detiene y dice en qué vuelta y con qué valores. Implementa la
600
+ [decisión 0010](docs/decisions/0010-var-y-while.md).
601
+
602
+ ### Añadido
603
+
604
+ - **Variables reasignables:** `var count = 0` y `count = count + 1`. El tipo de una `var` no cambia
605
+ nunca. `let` sigue siendo inmutable, y los parámetros también, para que `ensures` y los
606
+ diagnósticos vean siempre los valores con los que se llamó a la función. Una `var` de tipo
607
+ refinado conserva su garantía: se le asigna una constante válida o un valor comprobado con
608
+ `Tipo(x)`.
609
+ - **Bucles `while`** con dos cláusulas al principio del cuerpo, con la forma de los contratos y su
610
+ explicación opcional tras una coma:
611
+ - `decreases` (exactamente una): la medida, un `Int` que no baja de 0 y disminuye en cada vuelta.
612
+ - `invariant` (cero o más): condiciones que se cumplen antes de cada evaluación de la condición.
613
+ - Códigos nuevos: `E2021` (bucle sin medida, o con más de una), `E4005` (invariante incumplido) y
614
+ `E4006` (la medida no disminuye o es negativa), estos dos de categoría `contract`, con código de
615
+ salida `5`. Sus diagnósticos dicen la vuelta y los valores de todas las variables visibles.
616
+ - Un error dentro de un bucle (`E3xxx`, `E4xxx`) gana una nota por cada bucle que lo contiene:
617
+ `In turn 3 of the loop at line 6.`
618
+ - `nex test` ya no puede quedarse esperando por un bucle: el ejemplo queda en `error` con `E4006`.
619
+ - `nex inspect`: nodos `Var`, `While` y `LoopClause` (sigue siendo `nexora.inspect/2`). `nex symbols`:
620
+ campos `mutable` y `assignments`. `nex context`: campo `mutable` en las variables, campo `loops` al
621
+ final de cada función con la condición, los invariantes y la medida de cada bucle, valores
622
+ `mutable_variables`, `loops` y `loop_invariants` en `features`, y una nota cuando hay bucles.
623
+ - `E1103` sugiere cómo escribir con `while` lo que se intentaba con `for`, `break` o `continue`.
624
+ - Ejemplos `loops.nex` y `failing/loop_measure.nex`.
625
+
626
+ ### Cambiado
627
+
628
+ - `E2007` mantiene su código y su título, pero su etiqueta y su sugerencia dicen ahora si el nombre
629
+ es una variable `let` o un parámetro, y proponen `var`.
630
+ - La sugerencia de `E2014` recuerda que un bucle puede no ejecutarse nunca.
631
+
632
+ ### Cambios incompatibles
633
+
634
+ - **`invariant` y `decreases` pasan a ser palabras clave.** Un programa que los use como nombre de
635
+ variable, parámetro o función deja de ser válido (`E1101`); se renombran.
636
+ - `var` y `while` dejan de ser palabras reservadas. Ningún programa válido podía usarlas.
637
+
638
+ ### Documentación
639
+
640
+ - Especificación 0.5: sección 5.3 (`var` y la asignación) y sección 9.5 (bucles).
641
+ - Guías: bucles en [contratos](docs/contracts.md#bucles-invariant-y-decreases) y en
642
+ [primeros pasos](docs/getting-started.md).
643
+
644
+ ### Limitaciones
645
+
646
+ - No hay `for`, `break` ni `continue`, ni asignaciones compuestas (`+=`).
647
+ - La medida es un `Int`; no hay medidas compuestas.
648
+ - Una medida garantiza que un bucle termina, no que termine pronto: una medida inicial enorme
649
+ permite muchísimas vueltas. Una recursividad lenta (`fib(40)`) sigue sin límite de tiempo.
650
+
651
+ ## 0.4.0 — Ejemplos y `nex test`
652
+
653
+ Tercer paso de la línea principal: una función puede llevar casos concretos de lo que debe dar, y
654
+ `nex test` los comprueba sin escribir `main`. Implementa la
655
+ [decisión 0008](docs/decisions/0008-ejemplos-y-nex-test.md) y la
656
+ [decisión 0009](docs/decisions/0009-memoria-agotada.md).
657
+
658
+ ### Añadido
659
+
660
+ - **Ejemplos en las funciones:** `example double(2) == 4, "Two doubled"`, una cláusula del principio
661
+ del cuerpo junto a `requires` y `ensures`. Es una expresión `Bool` que tiene que llamar a su propia
662
+ función (`E2020` si no lo hace) y que no ve los parámetros ni `result`. El análisis nunca la evalúa.
663
+ - **`nex test`**: evalúa los ejemplos, cada uno en un intérprete nuevo, comprobando los contratos de
664
+ todo lo que se ejecuta. No exige `main` ni lo ejecuta. Opciones `--format text|json` y
665
+ `--function FUNCION`. Cada ejemplo termina en `passed`, `failed` (`E4004`, categoría nueva `test`) o
666
+ `error` (el diagnóstico original, con una nota que identifica el ejemplo).
667
+ - Código de salida `6`: algún ejemplo no pasó, o no había ningún ejemplo que evaluar.
668
+ - Formato `nexora.test/1` y `nexora.api.test_source`, que devuelve el mismo documento.
669
+ - `nex inspect`: nodo `Example` y campo `examples` al final de `Function` (sigue siendo
670
+ `nexora.inspect/2`). `nex context`: campo `examples` y valor `examples` en `features`.
671
+ - Ejemplo `examples/checked_examples.nex`.
672
+ - **`E3007` (Out of memory):** un programa que agota la memoria mientras se ejecuta recibe un
673
+ diagnóstico de ejecución, con la cadena de llamadas, en vez de un error interno. En `nex test`, el
674
+ ejemplo queda en `error` y se evalúan los siguientes. `nexora.api.run_source` sigue respondiendo
675
+ `memory_limit`, con el mismo mensaje, y ahora con el `E3007` en `diagnostics`.
676
+
677
+ ### Corregido
678
+
679
+ - **Las pruebas de npm y del servidor no se ejecutaban con Node 18 y 20.** `node --test` solo expande
680
+ patrones como `"test/*.test.js"` desde Node 21, así que con las versiones que declaran los paquetes
681
+ no encontraba ningún archivo. Los scripts nombran ahora cada archivo, y una prueba comprueba que no
682
+ falte ninguno. La integración continua prueba npm con Node 18, 20 y 22, y el servidor con Node 20.6
683
+ y 22.
684
+
685
+ ### Cambios incompatibles
686
+
687
+ - `example` pasa de palabra reservada a palabra clave. Ningún programa válido podía usarla como
688
+ nombre.
689
+ - **`nex run` con un programa que agota la memoria termina con `3`, no con `70`**, y la salida de
690
+ error muestra `runtime error[E3007]` en vez de `internal error`. Con `--format json` escribe el
691
+ documento `nexora.diagnostics/1`. El código `70` vuelve a significar solo un defecto de NEXORA.
692
+ Un script que trate `70` como "falta de memoria" debe mirar el código `E3007`.
693
+
694
+ ### Limitaciones
695
+
696
+ - `nex test` no tiene límite de tiempo ni de memoria por ejemplo.
697
+ - `E3007` solo aparece si Python llega a quedarse sin memoria. En Linux, el sistema puede matar el
698
+ proceso antes, sin ningún mensaje.
699
+ - Una memoria agotada fuera de la ejecución (al analizar el programa o al construir un informe)
700
+ sigue terminando con `70`.
701
+
702
+ ## 0.3.1 — Revisión de los tipos refinados
703
+
704
+ Resultado de intentar romper las garantías de la 0.3.0. La garantía principal resistió: ningún
705
+ camino permite que un valor llegue a un tipo refinado sin cumplir su condición, y la comprobación
706
+ sin ejecutar y la ejecución coinciden en los casos límite (`-0.0`, redondeo de `Float`, división
707
+ por cero evitada con `and`, desbordamiento). Esos ataques forman parte ahora de las pruebas.
708
+ La revisión encontró cuatro defectos alrededor de esa garantía, corregidos aquí.
709
+
710
+ ### Corregido
711
+
712
+ - **`nex check` podía tardar demasiado con tipos refinados.** Cada constante evalúa las condiciones
713
+ de toda la cadena de su tipo, así que el trabajo crecía con tipos × condiciones × constantes: un
714
+ programa válido de 60 KB tardaba 2,5 s y agotaba el tiempo del playground (2 s). Ahora el
715
+ trabajo está acotado a 100 000 nodos de condición evaluados; por encima, el programa se rechaza
716
+ con el código nuevo `E2019` en vez de quedar comprobado a medias. El mismo programa se rechaza
717
+ ahora en 0,18 s, y uno corriente se comprueba en centésimas.
718
+ - **Una condición de tipo mal escrita producía errores falsos en cada uso del tipo** ("Cannot apply
719
+ '+' to Bad and Int", "Expected Bad, found Int"). Las condiciones se comprueban ahora antes que las
720
+ firmas, y un tipo con la condición incorrecta queda fuera sin más errores.
721
+ - **Un error de ejecución dentro de una condición no decía dónde se pidió la comprobación.** Si
722
+ `Huge(n)` desbordaba dentro de la condición de `Huge`, el diagnóstico señalaba la declaración del
723
+ tipo y nada más. Ahora añade la nota `While checking that a value is a valid Huge, at line …`.
724
+ - **Una explicación con `\n` rompía el formato de texto del diagnóstico.** Una explicación es ahora
725
+ una sola línea: un `\n` en ella es el error `E1101`.
726
+
727
+ ### Documentación
728
+
729
+ - La especificación (sección 10.1), la guía del servidor MCP y la descripción de
730
+ `analysis_executes_code` decían que el análisis "no ejecuta nada". Ahora dicen con precisión lo
731
+ que hace: no ejecuta el programa, pero evalúa las condiciones de los tipos refinados sobre las
732
+ constantes, sin llamadas a funciones del programa y con el trabajo acotado.
733
+ - Nuevo límite en el documento de capacidades (`get_capabilities()` en Python y en el servidor
734
+ MCP): `max_refinement_steps`.
735
+
736
+ ## 0.3.0 — Contratos y tipos refinados
737
+
738
+ Consolida la característica que distingue a NEXORA: los requisitos forman parte verificable del
739
+ programa ([decisión 0006](docs/decisions/0006-requisitos-verificables.md)). Los contratos dejan de
740
+ ser experimentales, cada regla puede llevar su explicación y llegan los tipos refinados. Los motivos
741
+ de cada elección están en la [decisión 0007](docs/decisions/0007-contratos-estables-y-tipos-refinados.md).
742
+
743
+ Esta versión incluye también todo lo hecho después de la 0.2.0 que no llegó a publicarse con otro
744
+ número: documentación, pruebas, playground y herramientas de análisis.
745
+
746
+ ### Añadido
747
+
748
+ - **Tipos refinados:** `type Percent = Int where value >= 0 and value <= 100`. Todo valor de un
749
+ tipo refinado cumple su condición:
750
+ - Una constante usada como valor del tipo se comprueba **sin ejecutar** (`E2017`).
751
+ - Cualquier otro valor se comprueba explícitamente con `Percent(x)`, al ejecutar (`E4003`,
752
+ categoría `contract`, código de salida `5`).
753
+ - Un valor refinado se acepta donde se espera su base; una operación con él da un valor de la
754
+ base.
755
+ - Un tipo puede refinar otro (`type Pass = Percent where value >= 50`); se comprueban las dos
756
+ condiciones, desde la base.
757
+ - La condición solo puede usar `value`, literales, operadores y conversiones integradas
758
+ (`E2018`).
759
+ - **Explicación de las reglas:** `requires amount <= balance, "An account cannot go below zero"`.
760
+ Sirve en `requires`, `ensures` y `where`, y aparece en el diagnóstico como `Reason: ...` cuando
761
+ la regla se incumple.
762
+ - Códigos nuevos: `E2016` (declaración de tipo no válida), `E2017` (constante fuera del tipo
763
+ refinado), `E2018` (condición de refinamiento no válida) y `E4003` (refinamiento incumplido).
764
+ - `nex context` lista los tipos refinados (campo `types`) y la explicación de cada contrato (campo
765
+ `message`). Característica nueva en `features`: `refined_types`.
766
+ - Ejemplos `refined_types.nex`, `invalid/refinement_errors.nex` y
767
+ `failing/refinement_violation.nex`.
768
+ - **Licencia MIT** (`LICENSE`).
769
+ - **Paquete de npm** (`npm/`, nombre `nexora-lang`): instala el comando `nex` con un lanzador que
770
+ ejecuta el intérprete incluido usando el Python 3.11+ de la máquina. Preparado y probado en
771
+ local; **no publicado**.
772
+ - **`nex context`**: resumen de un programa (firmas, contratos, variables, llamadas entre
773
+ funciones, características usadas, diagnósticos), en texto o JSON. No ejecuta el programa.
774
+ - **`nex symbols`**: funciones, parámetros y variables con su tipo, su ámbito, su declaración y
775
+ cada uso, con los filtros `--name` y `--function`. No ejecuta el programa.
776
+ - **`nexora.tooling`**: API de Python con las mismas salidas (`check_source`, `inspect_source`,
777
+ `get_symbols`, `get_context`, `get_language_spec`, `get_capabilities`). **Experimental.**
778
+ - **Servidor MCP opcional** (`nex-mcp`, `pip install -e ".[mcp]"`): siete herramientas de análisis
779
+ para asistentes compatibles. No ejecuta código ni abre archivos indicados por quien llama.
780
+ Probado de forma automática con el cliente del SDK oficial (versión 2.3.0) y, a mano, en una
781
+ sesión de Claude Code 2.1.296 en Windows. No se ha probado en otros asistentes.
782
+ - Formatos `nexora.symbols/1`, `nexora.context/1`, `nexora.spec/1` y `nexora.capabilities/1`.
783
+ - JSON Schema de todos los formatos en `docs/schemas/`, generados desde el código y usados por las
784
+ pruebas para validar la salida real.
785
+ - Campo `nexora_version` en los documentos `nexora.diagnostics/1` y `nexora.inspect/1`. Es un campo
786
+ añadido: la versión del formato no cambia.
787
+ - Tareas de referencia para las herramientas de análisis (`tests/tooling_tasks.py`).
788
+ - **Análisis parcial con errores de sintaxis.** `nex inspect`, `nex symbols` y `nex context` (y sus
789
+ equivalentes en Python y en MCP) describen el código que sí pudo analizarse, con el campo nuevo
790
+ `complete` a `false`. Faltan las sentencias con errores y las funciones cuya declaración tiene
791
+ uno. Un programa con errores de sintaxis sigue sin ejecutarse.
792
+
793
+ - **Playground**: página web que comprueba y ejecuta código con el intérprete real
794
+ (`docs/playground.md`). Preparado para uso local; el aislamiento en contenedor para un servicio
795
+ público está configurado pero **no probado**.
796
+ - `nexora.api`: `check_source` y `run_source`, para comprobar o ejecutar código en memoria con
797
+ límites opcionales de salida y de longitud de texto. Esquema de resultado `nexora.result/1`.
798
+ - `python -m nexora.worker`: proceso de un solo uso que atiende una petición JSON.
799
+ - Diagnóstico `E3006` (texto demasiado largo), que solo aparece donde el entorno fija un límite.
800
+ - Código de salida `130` cuando la ejecución se interrumpe con Ctrl+C, en lugar de una traza de Python.
801
+ - Límite de 100 errores por etapa y archivo; el resumen del comando indica cuándo se alcanza.
802
+ - Ejemplos `arithmetic.nex` e `invalid/syntax_error.nex`.
803
+ - Pruebas: ejemplos con salida registrada, documentación ejecutable, seguridad y robustez.
804
+ - Medición de cobertura (`pytest-cov`) y análisis estático del código (`ruff`), como dependencias
805
+ de desarrollo.
806
+ - Configuración para generar un paquete instalable con el código, la documentación, los ejemplos
807
+ y las pruebas (`MANIFEST.in`).
808
+ - Flujo de integración continua para GitHub Actions. **No se ha ejecutado todavía.**
809
+
810
+ ### Corregido
811
+
812
+ - Un número entero literal de más de 4300 cifras provocaba un error interno. Ahora es el
813
+ diagnóstico `E1004`.
814
+ - Un archivo que no es NEXORA (por ejemplo, un binario) producía un error por carácter, sin límite.
815
+ - Cuando la pila se agotaba por llamadas recursivas dentro de expresiones muy anidadas, el mensaje
816
+ de `E3004` afirmaba que se habían superado 1000 llamadas aunque no fuera así. Ahora indica
817
+ cuántas había.
818
+ - Si el lector de la salida se cerraba antes de tiempo (`nex run … | head -1`), se mostraba un
819
+ error interno. Ahora `nex` termina en silencio.
820
+
821
+ ### Cambios incompatibles
822
+
823
+ - **`type` y `where` son palabras clave** (antes, palabras reservadas). Ningún programa válido de la
824
+ 0.2 podía usarlas como nombres, así que ninguno deja de funcionar.
825
+ - **`nex inspect` pasa a `nexora.inspect/2`.** El campo `type` de una expresión puede ser el nombre
826
+ de un tipo refinado, no solo `Int`, `Float`, `Bool`, `Text` o `Void`. Añadidos: nodo `TypeDecl`,
827
+ campo `types` en `Program` y campo `message` en `Contract`. Cómo actualizar: aceptar cualquier
828
+ texto en `type` y comprobar `schema`.
829
+ - Los contratos dejan de ser experimentales: su sintaxis no cambia, pero a partir de ahora solo
830
+ cambiará siguiendo las reglas de los cambios incompatibles.
831
+ - El paquete de Python pasa a llamarse `nexora-lang` (antes `nexora`), porque `nexora` ya existe en
832
+ PyPI. El comando (`nex`) y el módulo (`import nexora`) no cambian. Cómo actualizar una
833
+ instalación en modo desarrollo: `python -m pip uninstall nexora` y `python -m pip install -e .`.
834
+ Los archivos generados se llaman ahora `nexora_lang-0.3.0…`, y el extra opcional se instala con
835
+ `nexora-lang[mcp]`.
836
+ - En el documento de `nex inspect`, con errores de sintaxis, `program` es ahora un árbol parcial y
837
+ no `null`. Sigue siendo `null` con errores léxicos o si el archivo no puede leerse. Cómo
838
+ actualizar: para saber si un programa es válido, usa `ok`; para saber si el árbol cubre todo el
839
+ archivo, usa `complete`. El nombre del formato (`nexora.inspect/1`) no cambia; el motivo está en
840
+ `docs/json-protocol.md`.
841
+ - Los ejemplos incorrectos a propósito se han movido: `examples/errors.nex` es ahora
842
+ `examples/invalid/name_errors.nex`; `type_errors.nex` está en `examples/invalid/`;
843
+ `runtime_error.nex` y `contract_violation.nex`, en `examples/failing/`.
844
+
845
+ ### Retirado
846
+
847
+ - La función interna `values.type_name`, que no se usaba desde la 0.2.0.
848
+
849
+ ### Documentación
850
+
851
+ - Nuevas guías: instalación, primeros pasos, tipos, funciones, contratos, CLI, diagnósticos,
852
+ herramientas automáticas e IA, arquitectura, pruebas, limitaciones y hoja de ruta.
853
+ - `CONTRIBUTING.md`, `SECURITY.md` y `CODE_OF_CONDUCT.md`.
854
+ - Todos los programas de la documentación se ejecutan en las pruebas y su salida se compara con
855
+ la real.
856
+ - Especificación 0.3: sección 4.4 (tipos refinados), contratos estables (sección 9) y códigos
857
+ nuevos. Guías de tipos y contratos ampliadas. Decisiones 0006 y 0007.
858
+
859
+ ### Limitaciones
860
+
861
+ - De los tipos refinados, `nex check` solo comprueba las constantes; un valor calculado se comprueba
862
+ al ejecutar. No existe forma de comprobarlo sin detener el programa si falla (no hay `Result`).
863
+ - No hay alias de tipos sin condición.
864
+ - El proyecto sigue sin repositorio público y sin canal de contacto.
865
+ - Solo se ha comprobado en Windows 11 con Python 3.12.
866
+
867
+ ## 0.2.0
868
+
869
+ Sistema de tipos, funciones, contratos (experimentales) y salidas estructuradas para herramientas.
870
+
871
+ ### Añadido
872
+
873
+ - **Tipos:** anotaciones en `let` (`let age: Int = 25`), con inferencia cuando se omiten.
874
+ - **Operadores:** comparaciones `== != < <= > >=` y lógicos `and`, `or`, `not`.
875
+ - **Conversiones explícitas:** `to_float`, `to_int`, `to_text`.
876
+ - **Funciones** con parámetros tipados, tipo de retorno y `return`. Recursividad, con un máximo de
877
+ 1000 llamadas anidadas.
878
+ - **`if` / `else if` / `else`** como sentencia.
879
+ - **Contratos (experimental):** `requires` y `ensures`, con `result`.
880
+ - **Análisis estático** de tipos de argumentos y de retorno, rutas de retorno (`E2014`), código
881
+ inalcanzable (`E2015`) y valores devueltos sin usar.
882
+ - **`nex inspect`:** el árbol de sintaxis en JSON, con tipos y posiciones (`nexora.inspect/1`).
883
+ - **`nex run --format json`:** errores en JSON por la salida de error.
884
+ - **Categoría** en cada diagnóstico (`file`, `lexical`, `syntax`, `semantic`, `type`, `runtime`,
885
+ `contract`) y campo `schema` en los documentos JSON (`nexora.diagnostics/1`).
886
+ - Cadena de llamadas en los diagnósticos de ejecución.
887
+ - Códigos nuevos: `E1106`, `E2012`–`E2015`, `E3004`, `E3005`, `E4001`, `E4002`.
888
+ - Código de salida `5`: contrato incumplido.
889
+
890
+ ### Cambios incompatibles
891
+
892
+ - El tipo de texto se llama **`Text`** (antes `String`). Solo cambia en los mensajes: en 0.1 no se
893
+ podían escribir tipos.
894
+ - La cabecera de los diagnósticos de texto incluye la categoría:
895
+ `type error[E2011]: …` en lugar de `error[E2011]: …`.
896
+ - `to_text`, `to_float` y `to_int` ya no pueden usarse como nombres de variable.
897
+ - Un tipo de retorno desconocido es ahora `E2012` (antes `E2004`).
898
+ - Requiere Python 3.11 o superior (antes 3.10).
899
+
900
+ ### Retirado
901
+
902
+ - `E1104` y `E2003`, que avisaban de que los parámetros, las anotaciones y las funciones propias no
903
+ existían.
904
+
905
+ ### Compatibilidad
906
+
907
+ Los cuatro programas de ejemplo de 0.1 se ejecutan sin cambios y producen la misma salida.
908
+
909
+ ## 0.1.0
910
+
911
+ Primera versión ejecutable: función `main`, variables inmutables con `let`, tipos `Int`, `Float`,
912
+ `Bool` y `String`, aritmética, bloques, comentarios, `print`, y los comandos `nex run` y
913
+ `nex check`.