@saulwade/swl-ses 2.6.1 → 2.8.0

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 (268) hide show
  1. package/CLAUDE.md +14 -2
  2. package/README.md +65 -18
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/bin/swl-ses.js +10 -0
  6. package/comandos/swl/brainstorm.md +1 -0
  7. package/comandos/swl/briefing.md +119 -119
  8. package/comandos/swl/contribuir.md +233 -233
  9. package/comandos/swl/deuda-codigo.md +97 -97
  10. package/comandos/swl/mcp-status.md +1 -0
  11. package/gateway/lib/event-channel.js +191 -191
  12. package/habilidades/agent-deep-links/SKILL.md +148 -148
  13. package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
  14. package/habilidades/backend-error-design/SKILL.md +221 -221
  15. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  16. package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
  17. package/habilidades/calidad-contract-testing/SKILL.md +165 -165
  18. package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
  19. package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
  20. package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
  21. package/habilidades/css-moderno/SKILL.md +7 -1
  22. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  23. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  24. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
  25. package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
  26. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  27. package/habilidades/harness-claude-code/SKILL.md +3 -2
  28. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  29. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  30. package/habilidades/perfil-usuario/SKILL.md +200 -200
  31. package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
  32. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  33. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  34. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
  35. package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
  36. package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
  37. package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
  38. package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
  39. package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
  40. package/habilidades/proceso-modular-split/SKILL.md +256 -256
  41. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  42. package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
  43. package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
  44. package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
  45. package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
  46. package/hooks/calidad-pre-commit.js +159 -10
  47. package/hooks/ciclo-evolucion-subagente.js +26 -26
  48. package/hooks/ciclo-evolucion.js +26 -26
  49. package/hooks/contexto-subagente.js +68 -68
  50. package/hooks/lib/auto-consolidator.js +335 -335
  51. package/hooks/lib/ciclo-evolucion.js +47 -47
  52. package/hooks/lib/deep-links.js +185 -185
  53. package/hooks/lib/error-classifier.js +308 -308
  54. package/hooks/lib/notificacion-formato.js +45 -11
  55. package/hooks/lib/provenance-tracker.js +191 -191
  56. package/hooks/lib/raiz-proyecto.js +35 -4
  57. package/hooks/lib/resource-quota.js +122 -122
  58. package/hooks/lib/retry-jitter.js +165 -165
  59. package/hooks/lib/security-net.js +201 -201
  60. package/hooks/lib/skill-auditor.js +588 -588
  61. package/hooks/lib/sync-status.js +228 -228
  62. package/hooks/lib/taint-tracker.js +107 -107
  63. package/hooks/lib/text-similarity.js +241 -241
  64. package/hooks/lib/toon-compressor.js +245 -245
  65. package/hooks/notificacion-telegram.js +5 -11
  66. package/hooks/session-briefing.js +12 -4
  67. package/instintos/autonomia.yaml +27 -27
  68. package/instintos/prompt-appendices.yaml +57 -57
  69. package/llms.txt +1 -1
  70. package/manifiestos/agent-output-schemas.json +57 -57
  71. package/manifiestos/canonical-hashes.json +662 -0
  72. package/manifiestos/harness-ir.json +47536 -0
  73. package/manifiestos/hooks-config.json +469 -469
  74. package/manifiestos/invariantes-criticos.json +30 -30
  75. package/manifiestos/policy-bundle.json +2065 -0
  76. package/manifiestos/policy-corpus-w2.json +3926 -0
  77. package/manifiestos/runtime-adapters-core3.json +208 -0
  78. package/manifiestos/runtime-conformance.json +139 -0
  79. package/manifiestos/skills-lock.json +43 -43
  80. package/package.json +2 -2
  81. package/plantillas/auditor-veto-template.md +105 -105
  82. package/plantillas/github-workflows/release-please.yml +44 -44
  83. package/plantillas/github-workflows/swl-ci.yml +107 -107
  84. package/plantillas/github-workflows/swl-security.yml +51 -51
  85. package/plugin.json +2 -2
  86. package/reglas/accesibilidad.md +10 -10
  87. package/reglas/auditorias-documentales-estructurales.md +7 -7
  88. package/reglas/cloud-infra.md +8 -8
  89. package/reglas/consultar-vault-primero.md +195 -195
  90. package/reglas/git-workflow.md +1 -0
  91. package/reglas/hooks.md +6 -6
  92. package/reglas/intent-engineering.md +218 -218
  93. package/reglas/markitdown.md +8 -8
  94. package/reglas/monitor-ci.md +12 -0
  95. package/reglas/patrones.md +6 -6
  96. package/reglas/testing.md +7 -7
  97. package/reglas/tests-cleanup.md +224 -224
  98. package/schemas/agent-message.schema.json +73 -73
  99. package/schemas/agent-output-implementacion.schema.json +114 -114
  100. package/schemas/agent-output-planificacion.schema.json +150 -150
  101. package/schemas/agent-output-review.schema.json +98 -98
  102. package/schemas/diary-entry.schema.json +112 -112
  103. package/schemas/gate-state.schema.json +76 -0
  104. package/schemas/harness-ir.schema.json +369 -0
  105. package/schemas/hook-profiles.schema.json +54 -54
  106. package/schemas/hooks-config.schema.json +89 -89
  107. package/schemas/legacy-gates.schema.json +45 -0
  108. package/schemas/modulos.schema.json +38 -38
  109. package/schemas/perfiles.schema.json +36 -36
  110. package/schemas/plugin.schema.json +77 -77
  111. package/schemas/policy-bundle.schema.json +140 -0
  112. package/schemas/policy-enforcement.schema.json +117 -0
  113. package/schemas/policy-operation.schema.json +261 -0
  114. package/schemas/runtime-adapter.schema.json +176 -0
  115. package/schemas/runtime-build-attestation.schema.json +100 -0
  116. package/schemas/runtime-conformance.schema.json +239 -0
  117. package/schemas/runtime-diagnostic.schema.json +395 -0
  118. package/schemas/skill-evals.schema.json +119 -119
  119. package/schemas/skill-frontmatter.schema.json +245 -245
  120. package/schemas/w4-certification-request.schema.json +72 -0
  121. package/schemas/w4-certification-verdict.schema.json +224 -0
  122. package/schemas/w4-corpus.schema.json +172 -0
  123. package/schemas/w4-mutation-report.schema.json +116 -0
  124. package/schemas/w4-replay-result.schema.json +164 -0
  125. package/schemas/w4-scoring-report.schema.json +89 -0
  126. package/scripts/audit-tools/audit-history.js +330 -330
  127. package/scripts/audit-tools/bundle-tracker.js +290 -290
  128. package/scripts/audit-tools/canary-monitor.js +352 -352
  129. package/scripts/audit-tools/code-profiler.js +605 -605
  130. package/scripts/audit-tools/dep-doctor.js +320 -320
  131. package/scripts/audit-tools/env-validator.js +206 -206
  132. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  133. package/scripts/audit-tools/lib/output.js +23 -23
  134. package/scripts/audit-tools/migration-checker.js +392 -392
  135. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  136. package/scripts/bootstrap-instintos.js +3 -0
  137. package/scripts/cli/aprobar-plan.js +73 -73
  138. package/scripts/cli/briefing.js +23 -23
  139. package/scripts/cli/ciclo-evolucion.js +26 -26
  140. package/scripts/cli/derivar-feature-list.js +25 -25
  141. package/scripts/cli/detectar-host.js +27 -27
  142. package/scripts/cli/diary-entry.js +69 -69
  143. package/scripts/cli/execution-state.js +18 -18
  144. package/scripts/cli/gateway-notify.js +41 -41
  145. package/scripts/cli/liberar-fase.js +42 -42
  146. package/scripts/cli/mark-evolved.js +56 -56
  147. package/scripts/cli/metricas-dora.js +26 -26
  148. package/scripts/cli/near-duplicate.js +55 -55
  149. package/scripts/cli/notificaciones.js +123 -123
  150. package/scripts/cli/propose-step.js +29 -29
  151. package/scripts/cli/schedule-parse.js +19 -19
  152. package/scripts/cli/sugerir-modelo.js +20 -20
  153. package/scripts/cli/verificar-plan.js +36 -36
  154. package/scripts/cli/verificar-trazabilidad.js +35 -35
  155. package/scripts/comandos/install-asistido.js +8 -7
  156. package/scripts/configurar-branch-protection.js +418 -418
  157. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  158. package/scripts/doctor.js +61 -36
  159. package/scripts/generar-checklists-consolidados.js +273 -273
  160. package/scripts/generar-claims-runtime.js +1342 -0
  161. package/scripts/generar-harness-ir.js +257 -0
  162. package/scripts/generar-inventario.js +52 -54
  163. package/scripts/generar-policy-bundle.js +202 -0
  164. package/scripts/instalador.js +26 -7
  165. package/scripts/lib/approval-receipts.js +190 -0
  166. package/scripts/lib/artefactos-python.js +43 -43
  167. package/scripts/lib/benchmark-metrics.js +160 -160
  168. package/scripts/lib/budget-enforcer.js +252 -252
  169. package/scripts/lib/certificacion-loop-state.js +421 -0
  170. package/scripts/lib/ci-reader.js +193 -193
  171. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
  172. package/scripts/lib/clasificar-directorio.js +92 -0
  173. package/scripts/lib/contadores-inventario.js +217 -217
  174. package/scripts/lib/detectar-host-swl.js +175 -175
  175. package/scripts/lib/detectar-runtime.js +29 -20
  176. package/scripts/lib/detectar-stack-detallado.js +307 -307
  177. package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
  178. package/scripts/lib/detector-reglas-duplicadas.js +220 -220
  179. package/scripts/lib/eval-metrics-store.js +218 -218
  180. package/scripts/lib/eval-quality.js +171 -171
  181. package/scripts/lib/eval-schemas.js +144 -144
  182. package/scripts/lib/eval-self-correct.js +106 -106
  183. package/scripts/lib/eval-validator.js +185 -185
  184. package/scripts/lib/evidence-verifier.js +192 -0
  185. package/scripts/lib/evidencia-release.js +322 -322
  186. package/scripts/lib/frontmatter-canonico.js +509 -0
  187. package/scripts/lib/gate-engine.js +871 -0
  188. package/scripts/lib/gate-hooks-requires.js +249 -249
  189. package/scripts/lib/gate-licencias.js +212 -212
  190. package/scripts/lib/git-config-preflight.js +48 -0
  191. package/scripts/lib/git-metricas.js +257 -257
  192. package/scripts/lib/harness-ir.js +778 -0
  193. package/scripts/lib/harness-source-snapshot.js +309 -0
  194. package/scripts/lib/integrity-ledger.js +1147 -0
  195. package/scripts/lib/jaccard-similarity.js +98 -98
  196. package/scripts/lib/legacy-gate-migration.js +324 -0
  197. package/scripts/lib/limpiar-basura-global.js +45 -2
  198. package/scripts/lib/longmemeval-runner.js +125 -125
  199. package/scripts/lib/metricas-dora.js +204 -204
  200. package/scripts/lib/notificaciones-telegram.js +1 -0
  201. package/scripts/lib/npm-version.js +1 -0
  202. package/scripts/lib/paquetes-conocidos.js +50 -50
  203. package/scripts/lib/plan-lock.js +61 -13
  204. package/scripts/lib/policy-broker.js +338 -0
  205. package/scripts/lib/policy-bundle.js +342 -0
  206. package/scripts/lib/policy-context-provider.js +310 -0
  207. package/scripts/lib/policy-contract.js +479 -0
  208. package/scripts/lib/policy-verifier-utils.js +65 -0
  209. package/scripts/lib/pr-analyzer.js +399 -399
  210. package/scripts/lib/principal-verifier.js +178 -0
  211. package/scripts/lib/prompt-builder.js +264 -264
  212. package/scripts/lib/resolver-plan-fase.js +37 -37
  213. package/scripts/lib/rrf-fusion.js +175 -175
  214. package/scripts/lib/runtime-adapter-contract.js +267 -0
  215. package/scripts/lib/runtime-artifact-verifier.js +426 -0
  216. package/scripts/lib/runtime-build-attestation.js +127 -0
  217. package/scripts/lib/runtime-bundle-installer.js +586 -0
  218. package/scripts/lib/runtime-compiler.js +327 -0
  219. package/scripts/lib/runtime-conformance.js +202 -0
  220. package/scripts/lib/runtime-doctor-core3.js +567 -0
  221. package/scripts/lib/runtime-doctor-input.js +59 -0
  222. package/scripts/lib/runtime-operation-adapter.js +267 -0
  223. package/scripts/lib/schema-version.js +164 -164
  224. package/scripts/lib/semantic-search.js +252 -252
  225. package/scripts/lib/signed-envelope.js +545 -0
  226. package/scripts/lib/single-use-store.js +359 -0
  227. package/scripts/lib/skills-externas.js +31 -0
  228. package/scripts/lib/transformadores/codex.js +15 -8
  229. package/scripts/lib/transformadores/gemini.js +79 -5
  230. package/scripts/lib/w4-attestation-adapter.js +158 -0
  231. package/scripts/lib/w4-canario.js +337 -0
  232. package/scripts/lib/w4-claims.js +182 -0
  233. package/scripts/lib/w4-corpus-generador.js +542 -0
  234. package/scripts/lib/w4-gate-c5.js +115 -0
  235. package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
  236. package/scripts/lib/w4-matriz-combos.js +55 -0
  237. package/scripts/lib/w4-motor-mutacion.js +1348 -0
  238. package/scripts/lib/w4-motor-replay.js +735 -0
  239. package/scripts/lib/w4-pin-origen.js +54 -0
  240. package/scripts/lib/w4-publicar-request.js +132 -0
  241. package/scripts/lib/w4-revocacion.js +62 -0
  242. package/scripts/lib/w4-runtimes-core3.js +38 -0
  243. package/scripts/lib/w4-scorer-certificacion.js +692 -0
  244. package/scripts/lib/w4-superficie-candidato.js +49 -0
  245. package/scripts/lib/w4-veredicto.js +452 -0
  246. package/scripts/lib/w4-verificar-veredicto.js +302 -0
  247. package/scripts/limpiar-artefactos-python.js +131 -131
  248. package/scripts/migrar-csv-a-array.js +168 -168
  249. package/scripts/migrar-fase-dominio.js +200 -200
  250. package/scripts/migrar-gates-legacy.js +108 -0
  251. package/scripts/publicar-certification-request.js +115 -0
  252. package/scripts/runtime-doctor.js +107 -0
  253. package/scripts/tui/componentes/selector-multi.js +189 -189
  254. package/scripts/tui/componentes/selector-unico.js +158 -158
  255. package/scripts/tui/ejecutores.js +375 -375
  256. package/scripts/tui/lib/colores.js +129 -129
  257. package/scripts/tui/lib/render.js +264 -264
  258. package/scripts/tui/lib/teclas.js +113 -113
  259. package/scripts/tui/pantallas/install-wizard.js +12 -7
  260. package/scripts/tui/pantallas/menu-principal.js +52 -52
  261. package/scripts/tui/pantallas/progreso.js +274 -274
  262. package/scripts/tui/pantallas/resumen.js +132 -132
  263. package/scripts/validar-userland-vacio.js +110 -110
  264. package/scripts/verificar-aislamiento-swl-eval.js +87 -0
  265. package/scripts/verificar-empaquetado-downstream.js +375 -0
  266. package/scripts/verificar-loop-constructor.js +215 -0
  267. package/scripts/verificar-trazabilidad.js +13 -6
  268. package/scripts/verificar-veredicto-real.js +84 -0
@@ -0,0 +1,426 @@
1
+ 'use strict';
2
+
3
+ /** Verificación fail-closed de artefactos y locators runtime C10/W3. */
4
+
5
+ const crypto = require('node:crypto');
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+ const util = require('node:util');
9
+
10
+ const MAX_ARTIFACTS = 256;
11
+ const MAX_ARTIFACT_BYTES = 16 * 1024 * 1024;
12
+ const MAX_TOTAL_BYTES = 64 * 1024 * 1024;
13
+ const MAX_RELATIVE_PATH = 512;
14
+ const MAX_PATH_SEGMENT = 128;
15
+ const SHA256 = /^[a-f0-9]{64}$/u;
16
+ const SAFE_RELATIVE_PATH = /^[A-Za-z0-9._/-]+$/u;
17
+ const WINDOWS_RESERVED = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu;
18
+ const OPEN_FLAGS = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0);
19
+ const VERIFIED_SETS = new WeakMap();
20
+
21
+ function crearError(code, mensaje) {
22
+ const error = new Error(`${code}: ${mensaje}`);
23
+ error.code = code;
24
+ error.esErrorArtefactoSWL = true;
25
+ return error;
26
+ }
27
+
28
+ function esObjetoPlano(valor) {
29
+ if (valor === null || typeof valor !== 'object' || Array.isArray(valor)
30
+ || util.types.isProxy(valor)) return false;
31
+ const prototipo = Object.getPrototypeOf(valor);
32
+ return prototipo === Object.prototype || prototipo === null;
33
+ }
34
+
35
+ function esMaterialDatos(valor) {
36
+ if (Object.getOwnPropertySymbols(valor).length > 0) return false;
37
+ return Object.values(Object.getOwnPropertyDescriptors(valor))
38
+ .every((descriptor) => descriptor.enumerable && 'value' in descriptor);
39
+ }
40
+
41
+ function esArrayDatos(valor) {
42
+ if (!Array.isArray(valor) || util.types.isProxy(valor)
43
+ || Object.getOwnPropertySymbols(valor).length > 0) return false;
44
+ const descriptores = Object.getOwnPropertyDescriptors(valor);
45
+ const indices = Object.entries(descriptores).filter(([clave]) => clave !== 'length');
46
+ return indices.length === valor.length && indices.every(([clave, descriptor]) => (
47
+ /^(?:0|[1-9]\d*)$/u.test(clave) && descriptor.enumerable && 'value' in descriptor
48
+ ));
49
+ }
50
+
51
+ function assertShape(valor, campos, nombre) {
52
+ if (!esObjetoPlano(valor)) throw crearError('LOCATOR_INVALID', `${nombre} no es objeto plano`);
53
+ if (!esMaterialDatos(valor)) {
54
+ throw crearError('LOCATOR_INVALID', `${nombre} contiene material hostil`);
55
+ }
56
+ const faltante = campos.find((campo) => !Object.hasOwn(valor, campo));
57
+ if (faltante) throw crearError('LOCATOR_INVALID', `${nombre} omite ${faltante}`);
58
+ const extra = Object.keys(valor).find((campo) => !campos.includes(campo));
59
+ if (extra) throw crearError('LOCATOR_INVALID', `${nombre} contiene campo ${extra}`);
60
+ }
61
+
62
+ function rutasIguales(izquierda, derecha) {
63
+ return path.relative(izquierda, derecha) === '';
64
+ }
65
+
66
+ function mismaIdentidad(izquierda, derecha) {
67
+ return izquierda.dev === derecha.dev && izquierda.ino === derecha.ino;
68
+ }
69
+
70
+ function validarDirectorio(stats, code, nombre) {
71
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
72
+ throw crearError(code, `${nombre} debe ser directorio real`);
73
+ }
74
+ }
75
+
76
+ function cerrarDescriptores(descriptores) {
77
+ const unicos = [...new Set(descriptores.filter(Number.isInteger))];
78
+ const errores = [];
79
+ for (const descriptor of unicos) {
80
+ try { fs.closeSync(descriptor); } catch (error) { errores.push(error); }
81
+ }
82
+ return errores;
83
+ }
84
+
85
+ function cerrarRecursos(descriptores, errorOperacion) {
86
+ const erroresCierre = cerrarDescriptores(descriptores);
87
+ if (erroresCierre.length > 0) {
88
+ const error = crearError('CLOSE_FAILED', 'no se pudieron cerrar todos los descriptores');
89
+ error.cause = errorOperacion || erroresCierre[0];
90
+ throw error;
91
+ }
92
+ if (errorOperacion) throw errorOperacion;
93
+ }
94
+
95
+ function resolverRaiz(bundleRoot) {
96
+ if (typeof bundleRoot !== 'string' || !path.isAbsolute(bundleRoot)) {
97
+ throw crearError('ROOT_INVALID', 'la raíz absoluta es requerida');
98
+ }
99
+ const ruta = path.resolve(bundleRoot);
100
+ try {
101
+ const stats = fs.lstatSync(ruta, { bigint: true });
102
+ validarDirectorio(stats, 'ROOT_INVALID', 'la raíz');
103
+ if (!rutasIguales(ruta, fs.realpathSync(ruta))) {
104
+ throw crearError('ROOT_INVALID', 'la raíz contiene resolución por link');
105
+ }
106
+ return { ruta, stats };
107
+ } catch (error) {
108
+ if (error.code === 'ROOT_INVALID') throw error;
109
+ throw crearError('ROOT_INVALID', 'la raíz no existe o no es accesible');
110
+ }
111
+ }
112
+
113
+ function abrirRaiz(bundleRoot) {
114
+ const { ruta, stats: antes } = resolverRaiz(bundleRoot);
115
+ let descriptor;
116
+ try {
117
+ descriptor = fs.openSync(ruta, OPEN_FLAGS);
118
+ const handle = fs.fstatSync(descriptor, { bigint: true });
119
+ const despues = fs.lstatSync(ruta, { bigint: true });
120
+ validarDirectorio(handle, 'ROOT_CHANGED', 'la raíz abierta');
121
+ validarDirectorio(despues, 'ROOT_CHANGED', 'la raíz actual');
122
+ if (!mismaIdentidad(antes, handle) || !mismaIdentidad(handle, despues)) {
123
+ throw crearError('ROOT_CHANGED', 'la raíz cambió entre lstat y open');
124
+ }
125
+ return { ruta, descriptor, dev: handle.dev, ino: handle.ino, code: 'ROOT_CHANGED' };
126
+ } catch (error) {
127
+ const fallo = error.code === 'ROOT_CHANGED'
128
+ ? error
129
+ : crearError('OPEN_FAILED', 'no se pudo fijar la raíz');
130
+ cerrarRecursos([descriptor], fallo);
131
+ }
132
+ }
133
+
134
+ function validarDirectorioActual(handle) {
135
+ try {
136
+ const abierto = fs.fstatSync(handle.descriptor, { bigint: true });
137
+ const actual = fs.lstatSync(handle.ruta, { bigint: true });
138
+ validarDirectorio(abierto, handle.code, 'directorio abierto');
139
+ validarDirectorio(actual, handle.code, 'directorio actual');
140
+ if (!mismaIdentidad(handle, abierto) || !mismaIdentidad(handle, actual)) {
141
+ throw crearError(handle.code, 'directorio cambió de identidad');
142
+ }
143
+ } catch (error) {
144
+ if (error.code === handle.code) throw error;
145
+ throw crearError(handle.code, 'directorio desapareció o dejó de ser verificable');
146
+ }
147
+ }
148
+
149
+ function validarDirectorios(handles) {
150
+ handles.forEach(validarDirectorioActual);
151
+ }
152
+
153
+ function abrirDirectorio(ruta, padres) {
154
+ let descriptor;
155
+ let antes;
156
+ try {
157
+ antes = fs.lstatSync(ruta, { bigint: true });
158
+ } catch {
159
+ throw crearError('PATH_INVALID', 'ancestro ausente');
160
+ }
161
+ if (antes.isSymbolicLink()) throw crearError('LINK_REJECTED', 'symlink en path');
162
+ if (!antes.isDirectory()) throw crearError('PATH_INVALID', 'ancestro no es directorio');
163
+ try {
164
+ validarDirectorios(padres);
165
+ descriptor = fs.openSync(ruta, OPEN_FLAGS);
166
+ const abierto = fs.fstatSync(descriptor, { bigint: true });
167
+ const despues = fs.lstatSync(ruta, { bigint: true });
168
+ validarDirectorio(abierto, 'DIRECTORY_CHANGED', 'ancestro abierto');
169
+ validarDirectorio(despues, 'DIRECTORY_CHANGED', 'ancestro actual');
170
+ if (!mismaIdentidad(antes, abierto) || !mismaIdentidad(abierto, despues)) {
171
+ throw crearError('DIRECTORY_CHANGED', 'ancestro cambió entre lstat y open');
172
+ }
173
+ return { ruta, descriptor, dev: abierto.dev, ino: abierto.ino, code: 'DIRECTORY_CHANGED' };
174
+ } catch (error) {
175
+ const fallo = error.code === 'DIRECTORY_CHANGED'
176
+ ? error
177
+ : crearError('OPEN_FAILED', 'no se pudo fijar un ancestro');
178
+ cerrarRecursos([descriptor], fallo);
179
+ }
180
+ }
181
+
182
+ function validarSegmentoPortable(segmento) {
183
+ if (segmento.length > MAX_PATH_SEGMENT || /[. ]$/u.test(segmento)) {
184
+ throw crearError('PATH_INVALID', 'segmento excede longitud o termina en punto/espacio');
185
+ }
186
+ if (WINDOWS_RESERVED.test(segmento)) {
187
+ throw crearError('PATH_INVALID', 'nombre reservado no portable');
188
+ }
189
+ }
190
+
191
+ function validarPathRelativo(relativo) {
192
+ if (typeof relativo !== 'string' || relativo.length > MAX_RELATIVE_PATH
193
+ || !SAFE_RELATIVE_PATH.test(relativo)) {
194
+ throw crearError('PATH_INVALID', 'path relativo no canónico o excede longitud');
195
+ }
196
+ const partes = relativo.split('/');
197
+ if (path.posix.isAbsolute(relativo) || relativo.includes(':') || relativo.includes('\\')
198
+ || partes.some((parte) => parte === '' || parte === '.' || parte === '..')) {
199
+ throw crearError('PATH_INVALID', 'path absoluto, traversal, ADS o segmento inválido');
200
+ }
201
+ partes.forEach(validarSegmentoPortable);
202
+ return partes;
203
+ }
204
+
205
+ function validarListaRutas(rutas) {
206
+ if (!esArrayDatos(rutas) || rutas.length === 0) {
207
+ throw crearError('PATH_INVALID', 'la lista de paths debe ser datos no vacíos');
208
+ }
209
+ if (rutas.length > MAX_ARTIFACTS) throw crearError('LIMIT_EXCEEDED', 'cantidad de artefactos');
210
+ rutas.forEach(validarPathRelativo);
211
+ const ordenadas = [...rutas].sort();
212
+ const caseFold = rutas.map((ruta) => ruta.toLowerCase());
213
+ if (new Set(rutas).size !== rutas.length || new Set(caseFold).size !== rutas.length
214
+ || JSON.stringify(rutas) !== JSON.stringify(ordenadas)) {
215
+ throw crearError('PATH_INVALID', 'paths duplicados, con colisión case-fold o fuera de orden');
216
+ }
217
+ }
218
+
219
+ function abrirAncestros(raiz, partes) {
220
+ const handles = [raiz];
221
+ let actual = raiz.ruta;
222
+ try {
223
+ for (const parte of partes.slice(0, -1)) {
224
+ actual = path.join(actual, parte);
225
+ handles.push(abrirDirectorio(actual, handles));
226
+ }
227
+ return handles;
228
+ } catch (error) {
229
+ const abiertos = handles.slice(1).reverse().map(({ descriptor }) => descriptor);
230
+ cerrarRecursos(abiertos, error);
231
+ }
232
+ }
233
+
234
+ function identidadArchivo(stats) {
235
+ return [stats.dev, stats.ino, stats.size, stats.mtimeNs, stats.ctimeNs]
236
+ .map((valor) => String(valor)).join(':');
237
+ }
238
+
239
+ function validarStatsArchivo(stats) {
240
+ if (!stats.isFile()) throw crearError('TYPE_REJECTED', 'artefacto no es archivo regular');
241
+ if (stats.nlink !== 1n) throw crearError('LINK_REJECTED', 'hardlink detectado');
242
+ if (stats.size > BigInt(MAX_ARTIFACT_BYTES)) {
243
+ throw crearError('LIMIT_EXCEEDED', 'artefacto demasiado grande');
244
+ }
245
+ }
246
+
247
+ function leerBytes(descriptor, longitud) {
248
+ const bytes = Buffer.alloc(longitud);
249
+ let offset = 0;
250
+ while (offset < longitud) {
251
+ const leidos = fs.readSync(descriptor, bytes, offset, longitud - offset, offset);
252
+ if (leidos === 0) throw crearError('ARTIFACT_CHANGED', 'EOF antes del tamaño verificado');
253
+ offset += leidos;
254
+ }
255
+ return bytes;
256
+ }
257
+
258
+ function estaConfinado(raiz, destino) {
259
+ const relativo = path.relative(raiz, destino);
260
+ const primerSegmento = relativo.split(path.sep)[0];
261
+ return relativo !== '' && primerSegmento !== '..' && !path.isAbsolute(relativo);
262
+ }
263
+
264
+ function validarPathFinal(raiz, destino, statsHandle) {
265
+ try {
266
+ const real = fs.realpathSync(destino);
267
+ const statsPath = fs.lstatSync(destino, { bigint: true });
268
+ if (!estaConfinado(raiz, real) || statsPath.isSymbolicLink()) {
269
+ throw crearError('PATH_ESCAPE', 'path final fuera de la raíz o symlink');
270
+ }
271
+ if (!mismaIdentidad(statsPath, statsHandle)) {
272
+ throw crearError('ARTIFACT_CHANGED', 'path cambió de identidad durante lectura');
273
+ }
274
+ } catch (error) {
275
+ if (['PATH_ESCAPE', 'ARTIFACT_CHANGED'].includes(error.code)) throw error;
276
+ throw crearError('ARTIFACT_CHANGED', 'path desapareció durante lectura');
277
+ }
278
+ }
279
+
280
+ function abrirArchivo(ruta) {
281
+ try {
282
+ return fs.openSync(ruta, OPEN_FLAGS);
283
+ } catch {
284
+ throw crearError('OPEN_FAILED', 'no se pudo abrir el artefacto');
285
+ }
286
+ }
287
+
288
+ function leerArchivoEstable(raiz, relativo) {
289
+ const partes = validarPathRelativo(relativo);
290
+ const handles = abrirAncestros(raiz, partes);
291
+ const destino = path.join(raiz.ruta, ...partes);
292
+ let descriptor;
293
+ let bytes;
294
+ let errorOperacion;
295
+ try {
296
+ validarDirectorios(handles);
297
+ descriptor = abrirArchivo(destino);
298
+ const antes = fs.fstatSync(descriptor, { bigint: true });
299
+ validarStatsArchivo(antes);
300
+ validarDirectorios(handles);
301
+ bytes = leerBytes(descriptor, Number(antes.size));
302
+ const despues = fs.fstatSync(descriptor, { bigint: true });
303
+ validarDirectorios(handles);
304
+ if (identidadArchivo(antes) !== identidadArchivo(despues)
305
+ || antes.nlink !== despues.nlink) {
306
+ throw crearError('ARTIFACT_CHANGED', 'archivo cambió durante lectura');
307
+ }
308
+ validarStatsArchivo(despues);
309
+ validarPathFinal(raiz.ruta, destino, despues);
310
+ validarDirectorios(handles);
311
+ } catch (error) {
312
+ errorOperacion = error.esErrorArtefactoSWL
313
+ ? error
314
+ : crearError('OPEN_FAILED', 'falló la lectura por descriptor');
315
+ }
316
+ const ancestros = handles.slice(1).reverse().map((handle) => handle.descriptor);
317
+ cerrarRecursos([descriptor, ...ancestros], errorOperacion);
318
+ return bytes;
319
+ }
320
+
321
+ function metadataDe(ruta, bytes) {
322
+ return Object.freeze({
323
+ path: ruta,
324
+ bytes: bytes.length,
325
+ sha256: crypto.createHash('sha256').update(bytes).digest('hex'),
326
+ });
327
+ }
328
+
329
+ function registrarConjunto(entradas) {
330
+ const metadata = Object.freeze(entradas.map(({ path: ruta, bytes }) => metadataDe(ruta, bytes)));
331
+ const token = Object.freeze({ kind: 'verified-runtime-artifacts', entries: metadata });
332
+ const bytesPorRuta = entradas.map(({ path: ruta, bytes }) => [ruta, Buffer.from(bytes)]);
333
+ VERIFIED_SETS.set(token, new Map(bytesPorRuta));
334
+ return token;
335
+ }
336
+
337
+ function cargarArtefactosVerificados(bundleRoot, rutas) {
338
+ validarListaRutas(rutas);
339
+ const raiz = abrirRaiz(bundleRoot);
340
+ let total = 0;
341
+ let conjunto;
342
+ let errorOperacion;
343
+ try {
344
+ const entradas = rutas.map((ruta) => {
345
+ const bytes = leerArchivoEstable(raiz, ruta);
346
+ total += bytes.length;
347
+ if (total > MAX_TOTAL_BYTES) throw crearError('LIMIT_EXCEEDED', 'tamaño total excedido');
348
+ return { path: ruta, bytes };
349
+ });
350
+ validarDirectorioActual(raiz);
351
+ conjunto = registrarConjunto(entradas);
352
+ } catch (error) {
353
+ errorOperacion = error;
354
+ }
355
+ cerrarRecursos([raiz.descriptor], errorOperacion);
356
+ return conjunto;
357
+ }
358
+
359
+ function exigirConjunto(conjunto) {
360
+ const interno = VERIFIED_SETS.get(conjunto);
361
+ if (!interno) throw crearError('UNVERIFIED_SET', 'se requiere conjunto verificado');
362
+ return interno;
363
+ }
364
+
365
+ function esConjuntoArtefactosVerificado(valor) {
366
+ return VERIFIED_SETS.has(valor);
367
+ }
368
+
369
+ function crearLocator(conjunto) {
370
+ exigirConjunto(conjunto);
371
+ const entries = Object.freeze(conjunto.entries.map((entry) => Object.freeze({ ...entry })));
372
+ return Object.freeze({ schemaVersion: '1.0.0', kind: 'swl-runtime-locator', entries });
373
+ }
374
+
375
+ function validarEntradaLocator(entry, indice) {
376
+ assertShape(entry, ['path', 'bytes', 'sha256'], `locator entry ${indice}`);
377
+ validarPathRelativo(entry.path);
378
+ if (!Number.isSafeInteger(entry.bytes) || entry.bytes < 0 || entry.bytes > MAX_ARTIFACT_BYTES) {
379
+ throw crearError('LOCATOR_INVALID', 'bytes de locator inválidos');
380
+ }
381
+ if (typeof entry.sha256 !== 'string' || !SHA256.test(entry.sha256)) {
382
+ throw crearError('LOCATOR_INVALID', 'digest de locator inválido');
383
+ }
384
+ }
385
+
386
+ function validarLocator(locator) {
387
+ assertShape(locator, ['schemaVersion', 'kind', 'entries'], 'locator');
388
+ if (locator.schemaVersion !== '1.0.0' || locator.kind !== 'swl-runtime-locator') {
389
+ throw crearError('LOCATOR_INVALID', 'versión o kind de locator inválido');
390
+ }
391
+ if (!esArrayDatos(locator.entries) || locator.entries.length === 0) {
392
+ throw crearError('LOCATOR_INVALID', 'locator sin entradas de datos');
393
+ }
394
+ if (locator.entries.length > MAX_ARTIFACTS) {
395
+ throw crearError('LIMIT_EXCEEDED', 'cantidad de entradas del locator');
396
+ }
397
+ locator.entries.forEach(validarEntradaLocator);
398
+ validarListaRutas(locator.entries.map(({ path: ruta }) => ruta));
399
+ }
400
+
401
+ function verificarLocator(bundleRoot, locator) {
402
+ validarLocator(locator);
403
+ const conjunto = cargarArtefactosVerificados(
404
+ bundleRoot, locator.entries.map(({ path: ruta }) => ruta)
405
+ );
406
+ if (JSON.stringify(conjunto.entries) !== JSON.stringify(locator.entries)) {
407
+ throw crearError('ARTIFACT_MISMATCH', 'bytes o digest no coinciden con locator');
408
+ }
409
+ return conjunto;
410
+ }
411
+
412
+ function leerArtefactoVerificado(conjunto, relativo) {
413
+ const interno = exigirConjunto(conjunto);
414
+ const bytes = interno.get(relativo);
415
+ if (!bytes) throw crearError('PATH_INVALID', 'artefacto no pertenece al conjunto');
416
+ return Buffer.from(bytes);
417
+ }
418
+
419
+ module.exports = {
420
+ cargarArtefactosVerificados,
421
+ crearLocator,
422
+ esConjuntoArtefactosVerificado,
423
+ leerArtefactoVerificado,
424
+ validarListaRutasPortables: validarListaRutas,
425
+ verificarLocator,
426
+ };
@@ -0,0 +1,127 @@
1
+ 'use strict';
2
+
3
+ /** Registra una construcción Core-3 sin producir autoridad conductual. */
4
+
5
+ const util = require('node:util');
6
+
7
+ const { isKernelDependencies } = require('./policy-contract');
8
+ const { validarListaRutasPortables } = require('./runtime-artifact-verifier');
9
+ const { parsearIJson, validarTimestampIso } = require('./signed-envelope');
10
+
11
+ const MAX_RAW_BYTES = 256 * 1024;
12
+ const SHA256 = /^[a-f0-9]{64}$/u;
13
+ const PAYLOAD_KEYS = Object.freeze([
14
+ 'schemaVersion', 'kind', 'purpose', 'runtimeId', 'installerTarget', 'adapterId',
15
+ 'adapterSchemaVersion', 'contractVersion', 'manifestDigest', 'locatorDigest',
16
+ 'outputs', 'builtAt', 'effectiveMode',
17
+ ]);
18
+ const RUNTIMES = Object.freeze({
19
+ 'claude-code': 'claude',
20
+ 'codex-cli': 'codex',
21
+ 'gemini-cli': 'gemini',
22
+ });
23
+
24
+ function fail(codigo, mensaje) {
25
+ const error = new TypeError(mensaje);
26
+ error.codigo = codigo;
27
+ throw error;
28
+ }
29
+
30
+ function exactKeys(value, keys) {
31
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
32
+ && JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
33
+ }
34
+
35
+ function validateRawBytes(rawBytes) {
36
+ if (!rawBytes || typeof rawBytes !== 'object' || util.types.isProxy(rawBytes)) {
37
+ fail('BUILD_ATTESTATION_INVALID', 'bytes de atestación inválidos');
38
+ }
39
+ const prototype = Object.getPrototypeOf(rawBytes);
40
+ const validPrototype = prototype === Buffer.prototype || prototype === Uint8Array.prototype;
41
+ const intrusive = ['buffer', 'byteOffset', 'byteLength'].some(
42
+ (property) => Object.hasOwn(rawBytes, property)
43
+ );
44
+ if (!validPrototype || intrusive
45
+ || rawBytes.byteLength === 0 || rawBytes.byteLength > MAX_RAW_BYTES) {
46
+ fail('BUILD_ATTESTATION_INVALID', 'bytes de atestación inválidos');
47
+ }
48
+ }
49
+
50
+ function parsePayload(rawBytes) {
51
+ validateRawBytes(rawBytes);
52
+ try {
53
+ return parsearIJson(rawBytes);
54
+ } catch {
55
+ fail('BUILD_ATTESTATION_INVALID', 'atestación no es I-JSON canónico');
56
+ }
57
+ }
58
+
59
+ function validateOutputs(outputs) {
60
+ if (!Array.isArray(outputs) || !outputs.every((output) => (
61
+ exactKeys(output, ['path', 'sha256'])
62
+ && typeof output.path === 'string' && output.path.startsWith('outputs/')
63
+ && typeof output.sha256 === 'string' && SHA256.test(output.sha256)
64
+ ))) return false;
65
+ try {
66
+ validarListaRutasPortables(outputs.map((output) => output.path));
67
+ return true;
68
+ } catch {
69
+ return false;
70
+ }
71
+ }
72
+
73
+ function validateBindings(payload) {
74
+ if (!Object.hasOwn(RUNTIMES, payload.runtimeId)) return false;
75
+ return payload.installerTarget === RUNTIMES[payload.runtimeId]
76
+ && payload.adapterId === `runtime-adapter:${payload.runtimeId}`;
77
+ }
78
+
79
+ function validateConstants(payload) {
80
+ return payload.schemaVersion === '1.0.0'
81
+ && payload.kind === 'swl-runtime-build-attestation'
82
+ && payload.purpose === 'runtime-build'
83
+ && payload.adapterSchemaVersion === '1.0.0'
84
+ && payload.contractVersion === '1.0.0'
85
+ && payload.effectiveMode === 'proposal_only';
86
+ }
87
+
88
+ function validatePayload(payload) {
89
+ try {
90
+ validarTimestampIso(payload.builtAt, 'build timestamp');
91
+ } catch {
92
+ return false;
93
+ }
94
+ return exactKeys(payload, PAYLOAD_KEYS)
95
+ && validateConstants(payload)
96
+ && validateBindings(payload)
97
+ && SHA256.test(payload.manifestDigest)
98
+ && SHA256.test(payload.locatorDigest)
99
+ && validateOutputs(payload.outputs);
100
+ }
101
+
102
+ function validateCas(expectedSequence, expectedHeadHash) {
103
+ if (!Number.isSafeInteger(expectedSequence) || expectedSequence < 0
104
+ || Object.is(expectedSequence, -0) || typeof expectedHeadHash !== 'string'
105
+ || !SHA256.test(expectedHeadHash)) {
106
+ fail('BUILD_CAS_INVALID', 'precondición CAS inválida');
107
+ }
108
+ }
109
+
110
+ function registrarBuildAttestation(rawBytes, expectedSequence, expectedHeadHash, dependencies) {
111
+ if (arguments.length !== 4 || !isKernelDependencies(dependencies)) {
112
+ fail('BUILD_AUTHORITY_INVALID', 'dependencias W2 no verificadas');
113
+ }
114
+ validateCas(expectedSequence, expectedHeadHash);
115
+ const payload = parsePayload(rawBytes);
116
+ if (!validatePayload(payload)) {
117
+ fail('BUILD_ATTESTATION_INVALID', 'contrato de atestación inválido');
118
+ }
119
+ return dependencies.integrityLedger.append({
120
+ expectedSequence,
121
+ expectedHeadHash,
122
+ eventType: 'runtime-build',
123
+ payload,
124
+ });
125
+ }
126
+
127
+ module.exports = { registrarBuildAttestation };