@nac3/forge-cli 1.23.7 → 1.24.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 (162) hide show
  1. package/dist/bin/yf.js +0 -0
  2. package/dist/chat/claude.d.ts +8 -0
  3. package/dist/chat/claude.d.ts.map +1 -1
  4. package/dist/chat/claude.js.map +1 -1
  5. package/dist/chat/claude_cli.d.ts +23 -0
  6. package/dist/chat/claude_cli.d.ts.map +1 -1
  7. package/dist/chat/claude_cli.js +76 -2
  8. package/dist/chat/claude_cli.js.map +1 -1
  9. package/dist/chat/doctrine_digest.d.ts.map +1 -1
  10. package/dist/chat/doctrine_digest.js +2 -1
  11. package/dist/chat/doctrine_digest.js.map +1 -1
  12. package/dist/chat/server.d.ts.map +1 -1
  13. package/dist/chat/server.js +11 -0
  14. package/dist/chat/server.js.map +1 -1
  15. package/dist/chat/tools/pending.d.ts +11 -0
  16. package/dist/chat/tools/pending.d.ts.map +1 -1
  17. package/dist/chat/tools/pending.js +55 -8
  18. package/dist/chat/tools/pending.js.map +1 -1
  19. package/dist/chat/tools/shell.d.ts +39 -4
  20. package/dist/chat/tools/shell.d.ts.map +1 -1
  21. package/dist/chat/tools/shell.js +167 -7
  22. package/dist/chat/tools/shell.js.map +1 -1
  23. package/dist/chat/tools/urazuke.d.ts.map +1 -1
  24. package/dist/chat/tools/urazuke.js +8 -5
  25. package/dist/chat/tools/urazuke.js.map +1 -1
  26. package/dist/chat/urazuke_section.d.ts +26 -4
  27. package/dist/chat/urazuke_section.d.ts.map +1 -1
  28. package/dist/chat/urazuke_section.js +115 -9
  29. package/dist/chat/urazuke_section.js.map +1 -1
  30. package/dist/commands/pending.d.ts.map +1 -1
  31. package/dist/commands/pending.js +116 -12
  32. package/dist/commands/pending.js.map +1 -1
  33. package/dist/commands/urazuke.d.ts +3 -0
  34. package/dist/commands/urazuke.d.ts.map +1 -1
  35. package/dist/commands/urazuke.js +80 -3
  36. package/dist/commands/urazuke.js.map +1 -1
  37. package/dist/core/ascii_purity.d.ts +69 -0
  38. package/dist/core/ascii_purity.d.ts.map +1 -0
  39. package/dist/core/ascii_purity.js +141 -0
  40. package/dist/core/ascii_purity.js.map +1 -0
  41. package/dist/core/battery_stamp.d.ts +85 -0
  42. package/dist/core/battery_stamp.d.ts.map +1 -0
  43. package/dist/core/battery_stamp.js +237 -0
  44. package/dist/core/battery_stamp.js.map +1 -0
  45. package/dist/core/ci_gaps.d.ts +129 -0
  46. package/dist/core/ci_gaps.d.ts.map +1 -0
  47. package/dist/core/ci_gaps.js +352 -0
  48. package/dist/core/ci_gaps.js.map +1 -0
  49. package/dist/core/doc_citations.d.ts +133 -0
  50. package/dist/core/doc_citations.d.ts.map +1 -0
  51. package/dist/core/doc_citations.js +271 -0
  52. package/dist/core/doc_citations.js.map +1 -0
  53. package/dist/docs/doctrine/urazuke.md +173 -2
  54. package/dist/nac3/internal_manifest.d.ts.map +1 -1
  55. package/dist/nac3/internal_manifest.js +28 -2
  56. package/dist/nac3/internal_manifest.js.map +1 -1
  57. package/dist/pending/store.d.ts +29 -0
  58. package/dist/pending/store.d.ts.map +1 -1
  59. package/dist/pending/store.js +51 -1
  60. package/dist/pending/store.js.map +1 -1
  61. package/dist/pending/types.d.ts +23 -0
  62. package/dist/pending/types.d.ts.map +1 -1
  63. package/dist/pending/types.js.map +1 -1
  64. package/dist/telemetry/usage.d.ts +18 -0
  65. package/dist/telemetry/usage.d.ts.map +1 -1
  66. package/dist/telemetry/usage.js +16 -1
  67. package/dist/telemetry/usage.js.map +1 -1
  68. package/dist/urazuke/announce.d.ts +7 -0
  69. package/dist/urazuke/announce.d.ts.map +1 -1
  70. package/dist/urazuke/announce.js +20 -0
  71. package/dist/urazuke/announce.js.map +1 -1
  72. package/dist/urazuke/catalog.d.ts +1 -1
  73. package/dist/urazuke/catalog.d.ts.map +1 -1
  74. package/dist/urazuke/catalog.js.map +1 -1
  75. package/dist/urazuke/close_gate.d.ts +31 -4
  76. package/dist/urazuke/close_gate.d.ts.map +1 -1
  77. package/dist/urazuke/close_gate.js +70 -13
  78. package/dist/urazuke/close_gate.js.map +1 -1
  79. package/dist/urazuke/current.d.ts +28 -0
  80. package/dist/urazuke/current.d.ts.map +1 -1
  81. package/dist/urazuke/current.js +18 -5
  82. package/dist/urazuke/current.js.map +1 -1
  83. package/dist/urazuke/cycle.d.ts +3 -0
  84. package/dist/urazuke/cycle.d.ts.map +1 -1
  85. package/dist/urazuke/cycle.js +5 -3
  86. package/dist/urazuke/cycle.js.map +1 -1
  87. package/dist/urazuke/evidence.d.ts +14 -1
  88. package/dist/urazuke/evidence.d.ts.map +1 -1
  89. package/dist/urazuke/evidence.js +88 -2
  90. package/dist/urazuke/evidence.js.map +1 -1
  91. package/dist/urazuke/evidence_env.d.ts +78 -0
  92. package/dist/urazuke/evidence_env.d.ts.map +1 -0
  93. package/dist/urazuke/evidence_env.js +341 -0
  94. package/dist/urazuke/evidence_env.js.map +1 -0
  95. package/dist/urazuke/freeze.d.ts.map +1 -1
  96. package/dist/urazuke/freeze.js +16 -9
  97. package/dist/urazuke/freeze.js.map +1 -1
  98. package/dist/urazuke/gate.d.ts +48 -0
  99. package/dist/urazuke/gate.d.ts.map +1 -1
  100. package/dist/urazuke/gate.js +251 -25
  101. package/dist/urazuke/gate.js.map +1 -1
  102. package/dist/urazuke/gates_data.d.ts +30 -3
  103. package/dist/urazuke/gates_data.d.ts.map +1 -1
  104. package/dist/urazuke/gates_data.js +51 -5
  105. package/dist/urazuke/gates_data.js.map +1 -1
  106. package/dist/urazuke/gates_pull.d.ts +40 -0
  107. package/dist/urazuke/gates_pull.d.ts.map +1 -1
  108. package/dist/urazuke/gates_pull.js +118 -8
  109. package/dist/urazuke/gates_pull.js.map +1 -1
  110. package/dist/urazuke/manual_cases.d.ts +11 -1
  111. package/dist/urazuke/manual_cases.d.ts.map +1 -1
  112. package/dist/urazuke/manual_cases.js +1 -0
  113. package/dist/urazuke/manual_cases.js.map +1 -1
  114. package/dist/urazuke/overrides.d.ts +107 -0
  115. package/dist/urazuke/overrides.d.ts.map +1 -0
  116. package/dist/urazuke/overrides.js +128 -0
  117. package/dist/urazuke/overrides.js.map +1 -0
  118. package/dist/urazuke/probes.d.ts.map +1 -1
  119. package/dist/urazuke/probes.js +51 -0
  120. package/dist/urazuke/probes.js.map +1 -1
  121. package/dist/urazuke/profiles.d.ts +135 -0
  122. package/dist/urazuke/profiles.d.ts.map +1 -0
  123. package/dist/urazuke/profiles.js +470 -0
  124. package/dist/urazuke/profiles.js.map +1 -0
  125. package/dist/urazuke/receipt_audit.d.ts +67 -0
  126. package/dist/urazuke/receipt_audit.d.ts.map +1 -0
  127. package/dist/urazuke/receipt_audit.js +103 -0
  128. package/dist/urazuke/receipt_audit.js.map +1 -0
  129. package/dist/urazuke/receipts.d.ts +22 -0
  130. package/dist/urazuke/receipts.d.ts.map +1 -1
  131. package/dist/urazuke/receipts.js +44 -0
  132. package/dist/urazuke/receipts.js.map +1 -1
  133. package/dist/urazuke/report.d.ts +30 -0
  134. package/dist/urazuke/report.d.ts.map +1 -1
  135. package/dist/urazuke/report.js +39 -0
  136. package/dist/urazuke/report.js.map +1 -1
  137. package/dist/urazuke/reset.d.ts +35 -0
  138. package/dist/urazuke/reset.d.ts.map +1 -0
  139. package/dist/urazuke/reset.js +150 -0
  140. package/dist/urazuke/reset.js.map +1 -0
  141. package/dist/urazuke/retro.d.ts +5 -1
  142. package/dist/urazuke/retro.d.ts.map +1 -1
  143. package/dist/urazuke/retro.js +21 -8
  144. package/dist/urazuke/retro.js.map +1 -1
  145. package/dist/urazuke/section.d.ts.map +1 -1
  146. package/dist/urazuke/section.js +17 -2
  147. package/dist/urazuke/section.js.map +1 -1
  148. package/dist/urazuke/semantics.d.ts +173 -0
  149. package/dist/urazuke/semantics.d.ts.map +1 -0
  150. package/dist/urazuke/semantics.js +232 -0
  151. package/dist/urazuke/semantics.js.map +1 -0
  152. package/dist/urazuke/start.d.ts +8 -0
  153. package/dist/urazuke/start.d.ts.map +1 -1
  154. package/dist/urazuke/start.js +10 -2
  155. package/dist/urazuke/start.js.map +1 -1
  156. package/dist/version.d.ts +1 -1
  157. package/dist/version.js +1 -1
  158. package/dist/workflow/orchestrator.d.ts.map +1 -1
  159. package/dist/workflow/orchestrator.js +6 -7
  160. package/dist/workflow/orchestrator.js.map +1 -1
  161. package/docs/doctrine/urazuke.md +173 -2
  162. package/package.json +24 -20
@@ -0,0 +1,271 @@
1
+ /**
2
+ * LAS CITAS A LA NADA -- la compuerta que el dueno pidio (2026-11-01).
3
+ *
4
+ * EL DEFECTO, en sus palabras: "un documento que afirma una compuerta con
5
+ * confianza es mas convincente que uno que falta. La ausencia se nota; la
6
+ * afirmacion falsa no." Van tres citas a la nada medidas en dos lotes
7
+ * -- `docs/PROGRAMMING_NORMS.md` citado por `.github/workflows/ci.yml` cinco
8
+ * meses sin existir, cuatro jobs y dos crons descritos en
9
+ * `docs/CI_PIPELINE.md` que nunca existieron, y un comentario de
10
+ * `.github/workflows/ci.yml` al que remitia `CLAUDE.md` -- y las tres
11
+ * describian compuertas. Esta compuerta cierra la familia: todo archivo de
12
+ * este repositorio citado en un documento VIGENTE tiene que existir.
13
+ *
14
+ * POR QUE EL DETECTOR ES CONSERVADOR. Una compuerta ruidosa se desactiva, y
15
+ * un gancho desactivado es peor que ninguno porque figura como existente
16
+ * (el mismo argumento con el que el dueno acoto F-15). Asi que esto NO
17
+ * intenta encontrar toda cita: encuentra las que son inequivocamente una
18
+ * afirmacion sobre ESTE repositorio HOY. Las reglas, en orden:
19
+ *
20
+ * 1. Los bloques de codigo cercados no se miran. Un `ls docs/FOO.md` de
21
+ * ejemplo no es una afirmacion sobre el repositorio.
22
+ * 2. Solo cuentan los tramos entre acentos graves y los destinos de los
23
+ * enlaces markdown. Una ruta en prosa suelta no se toca.
24
+ * 3. Con `/`: el primer segmento tiene que ser un directorio que EXISTE en
25
+ * la raiz y no ser generado (`dist/`, `test-results/`, `.forge/`...).
26
+ * La cita afirma una UBICACION, asi que se verifica tal cual.
27
+ * Sin `/`: es un NOMBRE, no una ubicacion, y se verifica como tal --
28
+ * basta que un archivo con ese nombre exista en algun lugar del
29
+ * repositorio. Medido: tratar `claude.ts` como archivo de la raiz daba
30
+ * cientos de falsos positivos, y descartarlo del todo dejaba pasar
31
+ * nombres que ya no existen en ninguna parte (`relay_error.ts`).
32
+ * 4. Nada con comodin (`*`, `?`), hueco (`<x>`, `{x}`, `$x`), dos puntos
33
+ * (rutas de Windows, `repo:ruta`) ni esquema (`http://`). Son formas
34
+ * que no afirman un archivo concreto.
35
+ *
36
+ * Y LOS DOCUMENTOS HISTORICOS NO ENTRAN, por una razon que no es comodidad:
37
+ * un changelog, un traspaso o un informe fechado describen el estado del
38
+ * repositorio EN SU FECHA, y un archivo que existia entonces y se renombro
39
+ * despues es historia correcta, no una cita a la nada. Medido: de 2378 citas
40
+ * verificables, 762 estaban muertas y la enorme mayoria vivia justamente
41
+ * ahi. Meterlas obligaria a una lista de permisos de cientos de entradas sin
42
+ * motivo real, que es la misma afirmacion sin respaldo que esto persigue. La
43
+ * exclusion se DECLARA (`isHistoricalDoc`) y la corrida dice cuantos
44
+ * documentos salteo, nunca en silencio.
45
+ *
46
+ * ASCII-only.
47
+ */
48
+ /** Directorios de la raiz que existen pero NO son fuente: lo que hay dentro
49
+ * lo genera una corrida o lo escribe un producto en ejecucion, asi que un
50
+ * documento puede nombrarlos legitimamente sin que esten en disco. */
51
+ export const GENERATED_ROOTS = [
52
+ 'dist', 'node_modules', 'test-results', 'qa_reports', 'coverage', 'build',
53
+ ];
54
+ /** Extensiones que hacen que un token sin `/` cuente como cita de un nombre
55
+ * de archivo. Corta a proposito: un token cualquiera no es una cita. */
56
+ const NAME_EXT = [
57
+ '.md', '.json', '.yml', '.yaml', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
58
+ '.toml', '.sql', '.sh', '.py', '.php',
59
+ ];
60
+ /** Sufijos de puntuacion que la prosa deja pegados a una ruta. */
61
+ function trimPunctuation(s) {
62
+ return s.replace(/^[('"`]+/, '').replace(/[)'"`,.;:!?]+$/, '');
63
+ }
64
+ /** La forma de la cita, o el motivo por el que no es una cita verificable. */
65
+ export function classifyToken(token, rootDirs) {
66
+ const t = token.trim();
67
+ if (!t)
68
+ return { reject: 'vacio' };
69
+ if (/\s/.test(t))
70
+ return { reject: 'tiene espacios' };
71
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(t))
72
+ return { reject: 'es una URL' };
73
+ if (t.startsWith('#') || t.startsWith('mailto:'))
74
+ return { reject: 'es un ancla o un correo' };
75
+ if (/[*?<>{}$|]/.test(t))
76
+ return { reject: 'tiene comodin o hueco' };
77
+ if (t.includes(':'))
78
+ return { reject: 'tiene dos puntos (ruta ajena o de Windows)' };
79
+ if (t.includes('\\'))
80
+ return { reject: 'usa separador de Windows' };
81
+ if (t.includes('..'))
82
+ return { reject: 'sube de directorio' };
83
+ if (t.startsWith('/'))
84
+ return { reject: 'es absoluta' };
85
+ if (t.startsWith('~'))
86
+ return { reject: 'es del home del usuario' };
87
+ const clean = t.replace(/^\.\//, '').replace(/\/+$/, '');
88
+ if (!clean)
89
+ return { reject: 'vacio' };
90
+ const dot = clean.lastIndexOf('.');
91
+ const ext = dot > 0 ? clean.slice(dot).toLowerCase() : '';
92
+ if (clean.includes('/')) {
93
+ const segs = clean.split('/');
94
+ const first = segs[0];
95
+ if (!rootDirs.includes(first))
96
+ return { reject: 'su primer segmento no es un directorio de este repositorio' };
97
+ if (first.startsWith('.') && first !== '.github')
98
+ return { reject: 'vive en un directorio de estado local' };
99
+ /* Generado en CUALQUIER tramo, no solo en el primero: la salida de un
100
+ * build de CI (`apps/x/android/app/build/outputs/...`) la produce la
101
+ * corrida, asi que nombrarla no afirma un archivo en disco. */
102
+ if (segs.some((s) => GENERATED_ROOTS.includes(s)))
103
+ return { reject: 'vive en un directorio generado' };
104
+ return 'path';
105
+ }
106
+ if (!ext)
107
+ return { reject: 'no parece un archivo' };
108
+ if (!NAME_EXT.includes(ext))
109
+ return { reject: 'extension no reconocida' };
110
+ return 'name';
111
+ }
112
+ /** Las citas verificables de un documento markdown. */
113
+ export function extractCitations(md, rootDirs) {
114
+ const out = [];
115
+ const lines = md.split(/\r?\n/);
116
+ let fence = null;
117
+ for (let i = 0; i < lines.length; i++) {
118
+ const line = lines[i];
119
+ const fenceMatch = /^\s*(```+|~~~+)/.exec(line);
120
+ if (fenceMatch) {
121
+ const mark = fenceMatch[1];
122
+ if (fence === null)
123
+ fence = mark[0];
124
+ else if (mark[0] === fence)
125
+ fence = null;
126
+ continue;
127
+ }
128
+ if (fence !== null)
129
+ continue;
130
+ /* Lineas sangradas cuatro espacios: bloque de codigo indentado. */
131
+ if (/^ {4,}\S/.test(line) && !/^ {0,3}[-*+]/.test(line))
132
+ continue;
133
+ const tokens = [];
134
+ for (const m of line.matchAll(/`([^`\n]+)`/g))
135
+ tokens.push(m[1]);
136
+ for (const m of line.matchAll(/\]\(([^)\s]+)\)/g))
137
+ tokens.push(m[1]);
138
+ for (const raw of tokens) {
139
+ /* El ancla (`docs/X.md#seccion`) no es parte del archivo. */
140
+ const target0 = trimPunctuation(raw).split('#')[0];
141
+ const target = target0.replace(/^\.\//, '').replace(/\/+$/, '');
142
+ const shape = classifyToken(target, rootDirs);
143
+ if (typeof shape !== 'string')
144
+ continue;
145
+ if (out.some((c) => c.line === i + 1 && c.target === target))
146
+ continue;
147
+ out.push({ line: i + 1, target, raw, shape });
148
+ }
149
+ }
150
+ return out;
151
+ }
152
+ /** Un documento que describe el estado del repositorio en su fecha, no hoy.
153
+ * La lista es de FORMAS de nombre, no de rutas: una lista de rutas se
154
+ * desactualiza con cada documento nuevo y acaba siendo una lista de
155
+ * permisos. */
156
+ export function isHistoricalDoc(rel) {
157
+ const p = rel.replace(/\\/g, '/');
158
+ const base = p.split('/').pop();
159
+ if (base === 'CHANGELOG.md')
160
+ return true;
161
+ /* Carpetas de contexto volcado de una sesion. */
162
+ if (/(^|\/)context-from-/.test(p))
163
+ return true;
164
+ if (/(^|\/)handoff\//i.test(p))
165
+ return true;
166
+ /* Nombre con fecha: un documento fechado afirma sobre su fecha. Los dos
167
+ * separadores se mezclan de verdad en este repositorio
168
+ * (`CATCHUP_PRELIMINAR_2026-06-11.md`), asi que la forma los acepta
169
+ * mezclados en vez de pedir uno solo. */
170
+ if (/[_-]\d{4}[_-]\d{2}[_-]\d{2}/.test(base))
171
+ return true;
172
+ /* Generos que son registro, no especificacion vigente. */
173
+ if (/^(HANDOFF|SESSION|SESION|AUTOPILOT|CODE_REVIEW|INFORME|PROMPT|RETRO|SNAPSHOT|PENDING_REGISTRY_SNAPSHOT)[_-]/i.test(base))
174
+ return true;
175
+ return false;
176
+ }
177
+ /** Las citas de un archivo de MAQUINARIA (workflow, lefthook, manifiesto):
178
+ * ahi una ruta no viene entre acentos graves, viene en un comentario o en
179
+ * un valor. Se buscan por forma y se pasan por el mismo clasificador.
180
+ *
181
+ * Esto NO se usa sobre fuentes TypeScript a proposito: un especificador de
182
+ * import (`./tools/x.js`) tiene forma de ruta, es relativo al archivo y
183
+ * apunta al transpilado, asi que daria cientos de falsos positivos.
184
+ * Medido antes de acotar: 307.
185
+ *
186
+ * Y aca la cita tiene que llevar EXTENSION. En un archivo de maquinaria no
187
+ * hay acentos graves que delimiten el token, asi que un tramo sin extension
188
+ * suele ser otra cosa con forma de ruta -- medido: `src/version\.ts` dentro
189
+ * de una expresion regular de `lefthook.yml` se lee como `src/version`. Las
190
+ * tres citas a la nada que este repositorio ya pago llevaban extension. */
191
+ export function extractMachineryCitations(text, rootDirs) {
192
+ const out = [];
193
+ const lines = text.split(/\r?\n/);
194
+ const re = /[A-Za-z0-9_.][A-Za-z0-9_./-]*\/[A-Za-z0-9_][A-Za-z0-9_./-]*/g;
195
+ for (let i = 0; i < lines.length; i++) {
196
+ for (const m of lines[i].matchAll(re)) {
197
+ const target = trimPunctuation(m[0]).replace(/^\.\//, '').replace(/\/+$/, '');
198
+ if (!/\.[a-z0-9]{1,6}$/i.test(target))
199
+ continue;
200
+ const shape = classifyToken(target, rootDirs);
201
+ if (shape !== 'path')
202
+ continue;
203
+ if (out.some((c) => c.line === i + 1 && c.target === target))
204
+ continue;
205
+ out.push({ line: i + 1, target, raw: m[0], shape });
206
+ }
207
+ }
208
+ return out;
209
+ }
210
+ /** EL ALCANCE QUE BLOQUEA, y por que es este y no "todos los documentos".
211
+ *
212
+ * Medido en este repositorio: de 1331 citas verificables en documentos
213
+ * vigentes, 206 apuntan a archivos que no existen -- y casi todas son
214
+ * correctas. El espacio `docs/` esta SOBRECARGADO: `docs/SQ.md` exige
215
+ * `docs/USER_MANUAL.md` al proyecto ADOPTANTE, `src/urazuke/catalog.ts`
216
+ * nombra `docs/GATES.md` como artefacto que produce el proyecto que corre
217
+ * el metodo, y `docs/worker/README.md` cita `src/relay.js` relativo a su
218
+ * propio componente. Prescribir rutas para otros proyectos es el negocio
219
+ * de este repositorio, asi que "toda ruta citada existe" es FALSO como
220
+ * regla general y una compuerta que lo exija pide una lista de permisos de
221
+ * cientos de entradas -- que es la misma afirmacion sin respaldo que esto
222
+ * persigue.
223
+ *
224
+ * Lo que si se puede exigir sin excepciones es este conjunto: los archivos
225
+ * que se leen como AUTORIDAD sobre la maquinaria de este repositorio. Es
226
+ * donde vivieron los tres defectos medidos y es chico y verificable. El
227
+ * resto se MIDE y se informa en la misma salida, nunca en silencio.
228
+ *
229
+ * Y dentro de ese conjunto bloquean solo las citas con forma de RUTA. Un
230
+ * nombre suelto (`nac-bridge.js`, `pilot.bundle.js` en el README) no afirma
231
+ * una ubicacion de este repositorio: nombra un artefacto de otro paquete.
232
+ * Una ruta si afirma: dice "esta aca". Esa es la afirmacion que las tres
233
+ * citas a la nada hicieron y la que esto verifica. */
234
+ export const AUTHORITY_SCOPE = [
235
+ 'CLAUDE.md',
236
+ 'README.md',
237
+ 'PROJECT.md',
238
+ 'docs/PROGRAMMING_NORMS.md',
239
+ 'docs/CI_PIPELINE.md',
240
+ 'lefthook.yml',
241
+ 'package.json',
242
+ /* mas .github/workflows/*.yml, que el script expande del disco. */
243
+ ];
244
+ export function renderCitationReport(r) {
245
+ const out = [];
246
+ out.push('doc-citations [compuerta]: ' + r.authority_files + ' archivos de autoridad, '
247
+ + r.authority_checked + ' citas verificadas'
248
+ + (r.allowed ? ', ' + r.allowed + ' permitidas por declaracion' : '')
249
+ + ', ' + r.violations.length + ' a la nada.');
250
+ out.push('doc-citations [relevamiento, NO bloquea]: ' + r.survey_docs
251
+ + ' documentos vigentes (' + r.survey_skipped_historical + ' historicos salteados), '
252
+ + r.survey_checked + ' citas, ' + r.survey_dead + ' sin archivo en disco.');
253
+ out.push(' El relevamiento no bloquea porque este repositorio PRESCRIBE rutas para otros');
254
+ out.push(' proyectos (docs/SQ.md exige docs/USER_MANUAL.md al adoptante), asi que una ruta');
255
+ out.push(' sin archivo no es por si misma un defecto. El numero esta a la vista para que');
256
+ out.push(' nadie lo descubra midiendo de nuevo.');
257
+ if (!r.violations.length)
258
+ return out.join('\n');
259
+ out.push('');
260
+ out.push('CITAS A LA NADA EN EL ALCANCE QUE BLOQUEA (' + r.violations.length + '):');
261
+ for (const v of r.violations) {
262
+ out.push(' ' + v.doc + ':' + v.line + ' -> ' + v.target
263
+ + (v.shape === 'name' ? ' (no existe con ese nombre en ninguna parte)' : ' (no existe)'));
264
+ }
265
+ out.push('');
266
+ out.push('Un documento que afirma un archivo que no existe es peor que uno que falta:');
267
+ out.push('la ausencia se nota, la afirmacion falsa no. Arregla la cita, crea el archivo,');
268
+ out.push('o declara la excepcion con su motivo en docs/.doc-citations-allow.');
269
+ return out.join('\n');
270
+ }
271
+ //# sourceMappingURL=doc_citations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc_citations.js","sourceRoot":"","sources":["../../src/core/doc_citations.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,9 @@ stations (N0..N7) first and enters the loop at step 5.
18
18
  the step names exist, are not stubs, say what they must; the sizing
19
19
  record is admissible (G9); the plan items carry a spec; the evidence
20
20
  receipts exist. `yujin.pending.close` refuses a red step. An override
21
- needs the OWNER's written `force_reason` (kept in history); never pass
22
- one on your own initiative.
21
+ needs the OWNER's written `force_reason` AND its `force_kind` (kept in
22
+ history, counted per motive -- see "A forced close carries a TYPE");
23
+ never pass either on your own initiative.
23
24
  - Evidence is a RECEIPT Forge records (command, ref, exit code, counts),
24
25
  never your summary. "Tests pass" without a receipt is a claim.
25
26
  - A LONG run leaves its receipt the same way. `yujin.evidence.run` blocks
@@ -35,6 +36,123 @@ stations (N0..N7) first and enters the loop at step 5.
35
36
  receipt, `yujin.task.list` for a task. Two closes were reported as
36
37
  "believed" over a receipt that existed on disk the whole time.
37
38
 
39
+ ## When does a project EXPECT a ladder?
40
+
41
+ Written here because the condition must be findable, not inferred from the
42
+ source (the owner's condition on F-06, 2026-11-01): "if tomorrow a project
43
+ should have a ladder and does not leave that trace, the notice does not
44
+ come out and we are where we started. The condition has to be WRITTEN
45
+ where someone would look for it."
46
+
47
+ The switch being ON means **the method is available**, not **you are
48
+ walking it**. It is ON by default in Forge developer, so injecting the
49
+ step block -- or warning that it is missing -- in every project with the
50
+ switch on would charge ~700 characters of system prompt per turn to
51
+ everyone who does not use the method.
52
+
53
+ So Forge warns ("enabled and NOT arriving") only when the project leaves a
54
+ TRACE that it expects a ladder. Exactly three traces count, any one of
55
+ them is enough (`expectsLadder` in `src/chat/urazuke_section.ts`):
56
+
57
+ 1. **A Urazuke workflow instance exists** for the project, open or closed
58
+ (`findUrazukeInstance`). This is the normal trace: `yf urazuke start` /
59
+ `yujin.urazuke.start` leaves it.
60
+ 2. **The frozen copy of the gate definition** is on disk
61
+ (`.forge/urazuke/gates.json`, PND-165): the project pinned a version of
62
+ the method, so it is using it.
63
+ 3. **The method's artifacts are in the repository**: all three of
64
+ `docs/WORKING_AGREEMENT.md`, `docs/SEMANTIC_TREE.md` and `docs/GATES.md`
65
+ exist and are not stubs (`hasMethodArtifacts` in
66
+ `src/urazuke/probes.ts`).
67
+
68
+ With none of the three, the turn records `reason: 'not_in_use'` and stays
69
+ silent. That reason is itself the trace: it is in the result of
70
+ `buildUrazukeSection`, so "the notice did not come out" can always be
71
+ answered with WHICH condition failed. If you want the ladder in a project
72
+ that has none of the three, open it -- `yujin.urazuke.start` -- and the
73
+ step prompt arrives on the next turn.
74
+
75
+ ## A forced close carries a TYPE, and the type is counted
76
+
77
+ M-02, decided by the owner on 2026-11-01. A plan item ships only with its
78
+ receipts, and some items have no red to produce at all (documentation,
79
+ configuration). The answer is NOT a kind of pending that authorises
80
+ itself -- nothing can guarantee a code item will not be declared
81
+ "documentation" -- it is an honest forced close that is COUNTABLE:
82
+
83
+ - `force_reason` (the owner's prose, min 15 chars) is still mandatory, and
84
+ - `force_kind` is mandatory with it, from a CLOSED catalogue
85
+ (`src/urazuke/overrides.ts`; `yf pending close --force-kind`, or
86
+ `force_kind` on `yujin.pending.close`). A free-text type turns the
87
+ counter into a cloud of synonyms, which is not counting.
88
+
89
+ The condition that makes this worth having, in the owner's words: "the
90
+ typed motive has to be a NUMBER in the report. You have to be able to ask
91
+ how many closes were forced by `documentacion_sin_rojo_posible` and get an
92
+ answer. If the type exists and nobody counts it, it is the same hole with
93
+ a better name -- and worse, because it sounds solved."
94
+
95
+ So the override is stored as DATA on the item's history entry
96
+ (`history[].override = {gate, kind, missing}`), and
97
+ `docs/RETROSPECTIVE_<date>.md` prints one row per motive and one per gate
98
+ bypassed. Overrides recorded before this change are still counted, in
99
+ their own `sin tipo` row: discounting them would have made the total DROP
100
+ on the day of the fix, which is the worst way to measure a debt.
101
+
102
+ ## The catalogue's file names are PROTOCOL
103
+
104
+ Decided by the owner on 2026-11-01 (M-03, measured by the Urazuke
105
+ experiment), as a rule of the contract and not as an error message:
106
+
107
+ **The file names the step catalogue asks for are protocol and are NOT
108
+ translated. The CONTENT goes in the project's language.**
109
+
110
+ So a step that names `docs/ARCHITECTURE.md` is satisfied by a file at
111
+ exactly that path, whose prose may be entirely in Spanish. A
112
+ `docs/ARQUITECTURA.md` with the identical, correct content counts as
113
+ ABSENT -- and the gate now says so, with that reason, every time an
114
+ artifact is missing (`ARTIFACT_NAMES_ARE_PROTOCOL` in
115
+ `src/urazuke/gate.ts`), whether or not a similar-looking neighbour is
116
+ found next to it.
117
+
118
+ Why not a per-language alias table, which was the other option: **an alias
119
+ table is a second source of truth, and a second source of truth
120
+ desynchronises** -- the defect this project already has on record three
121
+ times. (Plus the price on the Forge side: 232 artifact names to maintain in
122
+ two languages.) It is the same line the `language-sovereignty` doctrine
123
+ draws for verb ids and code identifiers: translate the EXPERIENCE, never
124
+ the protocol.
125
+
126
+ What was unacceptable before this was never the requirement. It was the
127
+ SILENCE: a file with the right content next to it, counted as absent, with
128
+ no written reason.
129
+
130
+ ## The five statuses, and why `closed` is NOT `green`
131
+
132
+ A step carries ONE of five statuses, and two of them are easy to confuse --
133
+ the confusion cost a report that said 0 of 24 when three steps were closed
134
+ (F-07, measured by the Urazuke experiment, 2026-11-01):
135
+
136
+ | status | meaning |
137
+ |--------------|--------------------------------------------------------------|
138
+ | `closed` | the canonical task of this step IS CLOSED. Consolidated progress. The BEST state a step can be in. |
139
+ | `green` | the gate passes and the task is STILL OPEN: it CAN be closed, nobody closed it yet. |
140
+ | `red` | a check is missing. It does not close. |
141
+ | `owner` | nothing mechanical to check; the owner signs it. |
142
+ | `unmeasured` | the definition in force needs an evaluator this installation cannot measure. It does NOT close and it is NOT green. |
143
+
144
+ The two rules that follow:
145
+
146
+ 1. **`closed` and `green` are disjoint.** A closed step is reported as
147
+ `closed`, never as `green`. So **counting `green` to measure progress
148
+ LOSES every closed step** -- the best ones. Progress is `closed + green`
149
+ (`done_or_ready`), and a summary prints BOTH numbers, never one.
150
+ 2. **Nobody counts statuses on their own.** `summarizeLadder()` in
151
+ `src/urazuke/current.ts` is the only counter; the verb, the CLI, the
152
+ retrospective and the cyclic report all read it. A second, independent
153
+ count is exactly the inference the method warns against: it is how the
154
+ distinction gets lost again, in one consumer, silently.
155
+
38
156
  ## How to work inside it
39
157
 
40
158
  1. Read the URAZUKE section: red checks are what is missing. Produce the
@@ -48,3 +166,56 @@ stations (N0..N7) first and enters the loop at step 5.
48
166
  when / then" + `how_verified`) before it is implemented (step 5).
49
167
  5. No deadlines, ever: the method fixes the order and the gates, not the
50
168
  calendar. Speed is a consequence, not the objective.
169
+
170
+ ## The profiles axis: three evaluators, and where their data lives
171
+
172
+ Added 2026-11-01 (Pieza 6, D-044). The published definition requires these
173
+ three on the three anchors of the construction loop, and until that day this
174
+ installation could not measure any of them -- so adopting the published
175
+ definition froze three permanently unmeasurable steps.
176
+
177
+ | evaluator | step | what it measures |
178
+ |---|---|---|
179
+ | `pendings_profiles` | 5 | every open plan item that touches a screen declares its convergence group, the profiles that have it, and ONE acceptance criterion per profile |
180
+ | `profile_matrix` | 8 | the matrix is consistent, the navigation is DERIVED from it, and each profile's e2e reaches exactly its row |
181
+ | `manual_cases_per_profile` | 10 | every manual case ran once per affected profile, derived from the matrix |
182
+
183
+ The data, and the one rule that explains every choice -- **the matrix is the
184
+ producer**:
185
+
186
+ - `docs/PROFILE_MATRIX.md` carries the four columns of Annex H B2 and
187
+ nothing else: functionality, home, profiles, entry point. The table is
188
+ found by HEADER, so the column order is free.
189
+ - `.forge/profile-navigation.json` is the generated navigation, and it
190
+ declares a fingerprint. The evaluator **recomputes** that fingerprint from
191
+ the matrix and compares; it never reads the declared one and compares it
192
+ with itself, which would pass any file. The fingerprint is
193
+ self-describing (algorithm, encoding, canonical form, covers, excludes) --
194
+ one that cannot be reproduced from outside is not evidence.
195
+ - `.forge/profile-reachability.json` lists, per profile, what the e2e
196
+ **reached** (not what it attempted) and the id of its receipt. The
197
+ evaluator contrasts that id against the receipt registry: an artifact
198
+ whose receipt the registry does not have is prose.
199
+ - the two directions of "neither less nor more" fail DIFFERENTLY and never
200
+ merge into one red: reaching **less** than the row is a MISSING piece
201
+ (construction incomplete); reaching **more** is an AUTHORIZATION defect,
202
+ high severity by written definition. If the message does not name the
203
+ class, the reader under-rates it.
204
+ - the per-profile pending fields live in `PendingSpec` **in the registry**,
205
+ not in `docs/PENDING.md` (owner's decision, 2026-11-01): a gate cannot
206
+ measure prose, `pendings_spec` already measures there, and two sources for
207
+ the same datum age in opposite directions. The document is generated from
208
+ the registry or cites it. Write them with
209
+ `yf pending add --touches-screen --convergence-group ... --row-profile ...
210
+ --accept-for perfil=criterio`.
211
+ - `touches_screen` is a THREE-state field: absent means "not declared" and
212
+ the evaluator reports it as such -- never as "does not touch". Not looking
213
+ is not the same as nothing being there.
214
+ - the manual case ledger carries `functionality` (the link the method's
215
+ "derived from the matrix" implies): `yf urazuke rules sign <rule> passed
216
+ --by <name> --profile <id> --functionality "<row>"`. A case with a single
217
+ profile does NOT count as executed when its row has more.
218
+
219
+ To measure what adopting the published definition would cost BEFORE
220
+ adopting it: `yf urazuke rules show --published` (read only, writes
221
+ nothing, never blocks) and `yf urazuke gates pull`.
@@ -1 +1 @@
1
- {"version":3,"file":"internal_manifest.d.ts","sourceRoot":"","sources":["../../src/nac3/internal_manifest.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,SAAS,GACT,mBAAmB,GACnB,aAAa,GACb,eAAe,GACf,oBAAoB,CAAC;AAEzB,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,KAAK,GACL,YAAY,GACZ,SAAS,GACT,SAAS,GACT,cAAc,GACd,KAAK,CAAC;AAEV,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,YAAY,GACZ,SAAS,GACT,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QACzB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;QAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QAKnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB;;;4BAGwB;IACxB,YAAY,EAAE,OAAO,GAAG,QAAQ,CAAC;IACjC;;;;;;qCAMiC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAm7GD,wBAAgB,QAAQ,IAAI,YAAY,CAOvC;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAGzD;AAED;gEACgE;AAChE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAO9D;AAED;;;uDAGuD;AACvD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CA2CzF;AAED;;sEAEsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAO/D"}
1
+ {"version":3,"file":"internal_manifest.d.ts","sourceRoot":"","sources":["../../src/nac3/internal_manifest.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,SAAS,GACT,mBAAmB,GACnB,aAAa,GACb,eAAe,GACf,oBAAoB,CAAC;AAEzB,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,KAAK,GACL,YAAY,GACZ,SAAS,GACT,SAAS,GACT,cAAc,GACd,KAAK,CAAC;AAEV,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,YAAY,GACZ,SAAS,GACT,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QACzB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;QAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QAKnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB;;;4BAGwB;IACxB,YAAY,EAAE,OAAO,GAAG,QAAQ,CAAC;IACjC;;;;;;qCAMiC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAw8GD,wBAAgB,QAAQ,IAAI,YAAY,CAOvC;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAGzD;AAED;gEACgE;AAChE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAO9D;AAED;;;uDAGuD;AACvD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CA2CzF;AAED;;sEAEsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAO/D"}
@@ -14,6 +14,11 @@
14
14
  * ASCII-only.
15
15
  */
16
16
  import { VERSION as FORGE_VERSION } from '../version.js';
17
+ /* M-02: el catalogo de motivos de un cierre forzado se declara UNA vez
18
+ * (src/urazuke/overrides.ts) y el schema del verbo lo refleja. Repetir la
19
+ * lista aca es la segunda fuente de verdad que se desincroniza el dia que
20
+ * se agregue un motivo. */
21
+ import { OVERRIDE_KIND_NAMES } from '../urazuke/overrides.js';
17
22
  /* ============================================================
18
23
  * Verb declarations
19
24
  *
@@ -49,7 +54,7 @@ const VERBS = [
49
54
  type: 'object',
50
55
  properties: {
51
56
  title: { type: 'string', description: 'Tab header label. Max 80 chars.', minLength: 1, maxLength: 80 },
52
- kind: { type: 'string', description: 'Render mode. doc = DOCUMENTO (reporte/analisis/spec/ARQUITECTURA/DISEÑO): content = HTML por DEFAULT (h1/h2/h3, p, ul/ol, table, blockquote, pre, strong/em; NADA de <script>/<style>/JS -- se sanitiza), se renderiza dentro de una hoja con el look Yujin. Usa HTML siempre para documentos de arquitectura, diseño y specs (formato rico). Tambien acepta markdown como fallback. Usalo por DEFAULT para cualquier documento que generes. bpmn (o flow) = DIAGRAMA DE PROCESO BPMN 2.0 -- USALO cuando el usuario pide un FLUJO / flow / proceso / circuito de negocio (NO el grafo de nodos). content = JSON {lanes?:[{id,label}], nodes:[{id, type, label, lane?, gateway?}], flows:[{from, to, label?}]}; type en startEvent|endEvent|intermediate|task|userTask|serviceTask|scriptTask|sendTask|receiveTask|gateway; gateway en exclusive|parallel|inclusive. graph = grafo de nodos dinamico (relaciones/dependencias, NO procesos): content = JSON {nodes:[{id,label,weight,kind,detail}], edges:[{from,to,kind,label}]}; md/markdown = texto; code/table/tree. html = mini-navegador en iframe sandbox. app = mini-app con JS arbitrario (viz/herramienta) en iframe sandbox aislado; usa app solo si necesitas JS real, sino doc.', enum: ['code', 'table', 'markdown', 'md', 'html', 'app', 'bpmn', 'flow', 'document', 'doc', 'tree', 'graph'] },
57
+ kind: { type: 'string', description: 'Render mode. doc = DOCUMENTO (reporte/analisis/spec/ARQUITECTURA/DISENO): content = HTML por DEFAULT (h1/h2/h3, p, ul/ol, table, blockquote, pre, strong/em; NADA de <script>/<style>/JS -- se sanitiza), se renderiza dentro de una hoja con el look Yujin. Usa HTML siempre para documentos de arquitectura, diseno y specs (formato rico). Tambien acepta markdown como fallback. Usalo por DEFAULT para cualquier documento que generes. bpmn (o flow) = DIAGRAMA DE PROCESO BPMN 2.0 -- USALO cuando el usuario pide un FLUJO / flow / proceso / circuito de negocio (NO el grafo de nodos). content = JSON {lanes?:[{id,label}], nodes:[{id, type, label, lane?, gateway?}], flows:[{from, to, label?}]}; type en startEvent|endEvent|intermediate|task|userTask|serviceTask|scriptTask|sendTask|receiveTask|gateway; gateway en exclusive|parallel|inclusive. graph = grafo de nodos dinamico (relaciones/dependencias, NO procesos): content = JSON {nodes:[{id,label,weight,kind,detail}], edges:[{from,to,kind,label}]}; md/markdown = texto; code/table/tree. html = mini-navegador en iframe sandbox. app = mini-app con JS arbitrario (viz/herramienta) en iframe sandbox aislado; usa app solo si necesitas JS real, sino doc.', enum: ['code', 'table', 'markdown', 'md', 'html', 'app', 'bpmn', 'flow', 'document', 'doc', 'tree', 'graph'] },
53
58
  content: { type: 'string', description: 'Body text. Max 100000 chars. Para kind=graph: JSON {nodes,edges}. Para kind=bpmn/flow: JSON {lanes?,nodes,flows} (ver kind).', minLength: 1, maxLength: 100000 },
54
59
  language: { type: 'string', description: 'For kind=code: language hint.', maxLength: 40 },
55
60
  },
@@ -2962,6 +2967,19 @@ const VERBS = [
2962
2967
  blocker_who: { type: 'string', maxLength: 80 },
2963
2968
  priority: { type: 'number', description: '0..100 (0 = critical, 100 = nice-to-have). Default 50.' },
2964
2969
  related_doc: { type: 'string', maxLength: 200 },
2970
+ /* M-01 (experimento Urazuke, 2026-11-01) -- EL SPEC, por verbo.
2971
+ *
2972
+ * La compuerta del peldano 5 (gate.ts, evaluador `plan`) falla
2973
+ * cuando hay pendientes del plan SIN spec. El CLI podia escribirlo
2974
+ * (`yf pending add --acceptance ... --how-verified ...`) y el verbo
2975
+ * NO, asi que un agente que registraba un hallazgo durante el
2976
+ * recorrido volvia ROJO un peldano ya cerrado y no tenia manera de
2977
+ * arreglarlo. El metodo terminaba castigando la honestidad: el
2978
+ * camino barato era no anotar el hallazgo. Aparecio CUATRO veces en
2979
+ * la corrida. La forma es la del CLI, no una nueva. */
2980
+ acceptance: { type: 'array', description: 'Urazuke peldano 5: criterios de aceptacion, uno por entrada, en forma "dado / cuando / entonces". Sin esto el item cuenta como SIN spec y pone el peldano 5 en rojo.' },
2981
+ how_verified: { type: 'string', maxLength: 2000, description: 'Urazuke peldano 5: como se verifica SIN leer codigo. Va junto con acceptance: la compuerta exige los dos.' },
2982
+ flows: { type: 'array', description: 'Urazuke: los flujos de usuario que toca el item (los que el e2e del peldano 8 tiene que recorrer).' },
2965
2983
  },
2966
2984
  required: ['title', 'category'],
2967
2985
  },
@@ -3000,7 +3018,8 @@ const VERBS = [
3000
3018
  reason: { type: 'string', enum: ['shipped', 'obsoleted', 'duplicate', 'rejected'] },
3001
3019
  commit: { type: 'string', description: 'Git SHA that delivered the close. The affected sources are derived from it.', maxLength: 80 },
3002
3020
  resolution: { type: 'string', description: 'How it was resolved -- the closing history summary (what was done across the evolutive/corrective cycle). Saved as a history entry AND as the technical summary of the implementation record.', maxLength: 4000 },
3003
- force_reason: { type: 'string', maxLength: 500, description: 'Urazuke: ONLY when the owner explicitly decides to close a workflow task whose step is RED or out of order. The written reason (min 15 chars) is kept in the item history for the audit. Never use it on your own initiative.' },
3021
+ force_reason: { type: 'string', maxLength: 500, description: 'Urazuke: ONLY when the owner explicitly decides to close a workflow task whose step is RED or out of order. The written reason (min 15 chars) is kept in the item history for the audit. Never use it on your own initiative. Requires force_kind.' },
3022
+ force_kind: { type: 'string', maxLength: 60, description: 'Urazuke (M-02): the TYPE of the forced close, mandatory with force_reason so the retrospective can COUNT forced closes per motive. Closed catalogue.', enum: OVERRIDE_KIND_NAMES },
3004
3023
  functional: { type: 'string', description: 'FUNCTIONAL description of what was delivered: what the user can do now and how it behaves. Mandatory with reason=shipped -- it is not in the diff.', maxLength: 8000 },
3005
3024
  use_cases: { type: 'array', description: 'Concrete use cases now covered, one per entry.' },
3006
3025
  files: { type: 'array', description: 'Extra affected sources. Normally empty: with `commit` they come from the SHA.' },
@@ -3057,6 +3076,13 @@ const VERBS = [
3057
3076
  blocker_who: { type: 'string', maxLength: 80 },
3058
3077
  priority: { type: 'number', description: '0..100' },
3059
3078
  related_doc: { type: 'string', maxLength: 200 },
3079
+ /* M-01 -- el spec tambien por EDIT, no solo por add: un item
3080
+ * abierto antes de tener spec tiene que poder adquirirlo sin pasar
3081
+ * por la consola. Pasar uno de los dos campos reemplaza el spec
3082
+ * entero; no pasar ninguno lo deja intacto. */
3083
+ acceptance: { type: 'array', description: 'Urazuke peldano 5: criterios de aceptacion "dado / cuando / entonces", uno por entrada. Pasarlos REEMPLAZA el spec del item.' },
3084
+ how_verified: { type: 'string', maxLength: 2000, description: 'Urazuke peldano 5: como se verifica sin leer codigo. Va junto con acceptance.' },
3085
+ flows: { type: 'array', description: 'Urazuke: los flujos de usuario que toca el item.' },
3060
3086
  },
3061
3087
  required: ['id'],
3062
3088
  },