kcreate-scaffolding-gcp-script 0.0.0-stage → 0.2.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.
- package/LICENSE +21 -0
- package/README.md +370 -2
- package/bin/build.js +70 -0
- package/bin/cli.js +441 -0
- package/package.json +36 -4
- package/skill/SKILL.md +246 -0
- package/skill/references/lessons-learned.md +623 -0
- package/skill/scripts/01_init_config.sh +151 -0
- package/skill/scripts/02_print_config.sh +60 -0
- package/skill/scripts/10_build_components.sh +584 -0
- package/skill/scripts/20_validate_file_parity.sh +107 -0
- package/skill/scripts/21_validate_project_pinning.sh +105 -0
- package/skill/scripts/lib/common.sh +747 -0
- package/skill/scripts/run_tracking.sh +72 -0
- package/skill/templates/scaffolding.config.template +81 -0
- package/vendor/github-proyect-tracking/SKILL.md +171 -0
- package/vendor/github-proyect-tracking/references/api.md +163 -0
- package/vendor/github-proyect-tracking/references/architecture.md +171 -0
- package/vendor/github-proyect-tracking/scripts/tracking.py +196 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/__init__.py +1 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/commands.py +846 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/common.py +163 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/events.py +204 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/github.py +315 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/state.py +379 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# Lecciones reales incorporadas en este skill
|
|
2
|
+
|
|
3
|
+
Cinco fuentes:
|
|
4
|
+
|
|
5
|
+
- **A — descubiertas desplegando `xoloshop-qa` a mano** (sesión previa,
|
|
6
|
+
documentadas también en `docs/CreateScriptsNewGCPProjectInstructions.md`
|
|
7
|
+
y `docs/DeployNewGCPProjectInstructions.md` de `club-tijuana/infra-cloud`).
|
|
8
|
+
- **B — descubiertas probando este skill de punta a punta** contra una
|
|
9
|
+
copia descartable de `infra-cloud` real, generando `xoloshop-prod` desde
|
|
10
|
+
`boletera-qa`, antes de considerarlo terminado. Varias de estas B
|
|
11
|
+
**nunca se habían notado** ni siquiera en el proceso manual (ej. los
|
|
12
|
+
archivos de nivel raíz, el dominio DNS) — la automatización las hizo
|
|
13
|
+
visibles por primera vez.
|
|
14
|
+
- **C — encontradas por `Review_V1.md`**, una revisión independiente que
|
|
15
|
+
también ejecutó el skill de punta a punta (contra `boletera-qa` y, de
|
|
16
|
+
forma crucial, contra `xoloshop-qa` — el entorno que las pruebas B no
|
|
17
|
+
habían usado como fuente). Ver `Review_V1.md` en la raíz de este repo
|
|
18
|
+
para el texto completo de la revisión y la respuesta punto por punto.
|
|
19
|
+
- **D — encontradas por `Review_V2.md`**, una re-revisión después de
|
|
20
|
+
aplicar C, con reintentos reales sobre entornos a medio construir
|
|
21
|
+
(encontró una regresión crítica del propio fix de reanudación de C1).
|
|
22
|
+
Ver `Review_V2.md`.
|
|
23
|
+
- **E — encontradas por `Review_V3.md`**, una tercera revisión que
|
|
24
|
+
reprodujo la cadena crítica de N-1 paso a paso y encontró que el
|
|
25
|
+
propio fix de `artifact_registry` (una función nueva de esa misma
|
|
26
|
+
ronda) se había quedado con la misma contaminación que D1/D2 ya habían
|
|
27
|
+
corregido en las demás. Ver `Review_V3.md`.
|
|
28
|
+
|
|
29
|
+
Cada lección dice **qué pasaba**, **por qué**, y **en qué script/función
|
|
30
|
+
de este skill queda resuelta**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## A1 — Fórmula de CIDR sin validar produce octetos inválidos
|
|
35
|
+
|
|
36
|
+
**Qué pasaba:** extender a mano `10.N.0.0/22` sin revisar los 4 bloques
|
|
37
|
+
derivados produjo `10.335.0.0/17` (octeto `335`, inválido).
|
|
38
|
+
**Resuelto en:** `lib/common.sh:ksc_cidr_pick_free` — usa
|
|
39
|
+
`ipaddress.ip_network()` de Python (nunca aritmética a mano) y prueba
|
|
40
|
+
`N=1..249` hasta encontrar uno sin colisión; si no hay ninguno libre,
|
|
41
|
+
falla con un mensaje explícito en vez de devolver algo inválido.
|
|
42
|
+
|
|
43
|
+
## A2 — Coma faltante editando `project_services` a mano
|
|
44
|
+
|
|
45
|
+
**Qué pasaba:** un elemento sin coma al final de la lista rompe
|
|
46
|
+
`terragrunt hcl validate` con un error de sintaxis poco claro.
|
|
47
|
+
**Resuelto en:** `build_component_project_services` — el archivo se copia
|
|
48
|
+
**completo, sin tocarlo**. Este skill nunca edita esa lista campo por
|
|
49
|
+
campo, así que esta clase de bug queda eliminada por diseño, no detectada
|
|
50
|
+
después.
|
|
51
|
+
|
|
52
|
+
## A3 — Valores genéricos/org-wide en `artifact_registry`
|
|
53
|
+
|
|
54
|
+
**Qué pasaba:** `repository_id`/`description` quedaron como
|
|
55
|
+
`"club-tijuana-docker-repo"` / `"Official Club Tijuana docker
|
|
56
|
+
repository."` en un despliegue real, sin adaptar.
|
|
57
|
+
**Resuelto en:** `build_component_artifact_registry` — sustituye el
|
|
58
|
+
nombre de la app si lo encuentra en el valor. **Lección B encontrada
|
|
59
|
+
probando esto:** `boletera-qa`, la fuente real usada para probar este
|
|
60
|
+
skill, **todavía tiene ese mismo valor genérico sin corregir**. Como no
|
|
61
|
+
hay substring de la app para reemplazar, el script detecta el caso y
|
|
62
|
+
emite un `[WARN]` explícito en vez de copiar el problema en silencio —
|
|
63
|
+
nunca asume que "no encontré nada que sustituir" significa "está bien".
|
|
64
|
+
|
|
65
|
+
## A4 — IP de `cloudadmin` transcrita mal
|
|
66
|
+
|
|
67
|
+
**Qué pasaba:** `177.226.233.64` en vez de `177.226.232.136` al copiar a
|
|
68
|
+
mano.
|
|
69
|
+
**Resuelto en:** `01_init_config.sh` extrae las entradas fijas (no
|
|
70
|
+
`dev-*`) de `authorized_networks` directo del `terragrunt.hcl` real de
|
|
71
|
+
`SOURCE_ENV` con `grep`/`sed`, nunca de memoria ni de una tabla.
|
|
72
|
+
|
|
73
|
+
## A5 — IP de `dev-<usuario>` vieja o reusada
|
|
74
|
+
|
|
75
|
+
**Qué pasaba:** el ISP cambia; una IP de otra sesión queda obsoleta y
|
|
76
|
+
deja al usuario sin acceso.
|
|
77
|
+
**Resuelto en:** `lib/common.sh:ksc_fresh_public_ip`, llamada dentro de
|
|
78
|
+
`build_component_sql_postgres` — siempre `curl` en el momento de
|
|
79
|
+
construir, nunca cacheada entre corridas.
|
|
80
|
+
|
|
81
|
+
## A6 — `gke_version`/`database_version` copiados a ciegas
|
|
82
|
+
|
|
83
|
+
**Qué pasaba:** no verificar si ya existe una versión estable más
|
|
84
|
+
reciente antes de copiar la de `SOURCE_ENV`.
|
|
85
|
+
**Resuelto en:** el config los autodetecta con el sufijo
|
|
86
|
+
`_NEEDS_VERIFICATION` en el nombre del campo — el skill los usa tal cual
|
|
87
|
+
estén en el config al construir, pero el nombre del campo mismo recuerda
|
|
88
|
+
que deben verificarse contra la documentación oficial antes de confiar en
|
|
89
|
+
ellos ciegamente (esto sí requiere criterio humano/del agente — no es
|
|
90
|
+
mecanizable sin consultar una fuente externa).
|
|
91
|
+
|
|
92
|
+
## A7 — Falta `orgpolicy.googleapis.com` + sus 3 overrides
|
|
93
|
+
|
|
94
|
+
**Qué pasaba:** sin esta API y sus 3 overrides en `org_policies`, el
|
|
95
|
+
despliegue falla varios pasos después, con minutos de espera por
|
|
96
|
+
propagación cada vez que se corrige una política a la vez.
|
|
97
|
+
**Resuelto en:** `build_component_project_services` y
|
|
98
|
+
`build_component_org_policies` copian ambos archivos **completos** de
|
|
99
|
+
`SOURCE_ENV` — si `SOURCE_ENV` ya tiene la API y los 3 overrides (todo
|
|
100
|
+
entorno desplegado después de 2026-02-17 los tiene), `NEW_ENV` los hereda
|
|
101
|
+
automáticamente, sin que este skill necesite saber los nombres exactos de
|
|
102
|
+
las constraints.
|
|
103
|
+
|
|
104
|
+
## A8 — `roles/orgpolicy.policyAdmin` en `project_iam`
|
|
105
|
+
|
|
106
|
+
**Qué pasaba:** GCP rechaza ese rol a nivel proyecto
|
|
107
|
+
(`Role ... is not supported for this resource`); solo existe a nivel
|
|
108
|
+
Organización/Carpeta.
|
|
109
|
+
**Resuelto en:** `build_component_project_iam` hace `grep` de ese string
|
|
110
|
+
después de copiar y **falla la construcción** (no solo advierte) si lo
|
|
111
|
+
encuentra — a diferencia de los demás `[WARN]`, este es un error real
|
|
112
|
+
conocido, no una zona gris.
|
|
113
|
+
|
|
114
|
+
## A9 — Cambiar el casing de `project_name` sin que lo pidieran
|
|
115
|
+
|
|
116
|
+
**Qué pasaba:** confusión real entre `"XoloShop-QA"` y `"Xoloshop-QA"` en
|
|
117
|
+
el mismo PR.
|
|
118
|
+
**Resuelto en:** `lib/common.sh:ksc_infer_project_name` aplica una regla
|
|
119
|
+
fija y determinística (primera letra de la app en mayúscula, entorno
|
|
120
|
+
`QA`/`Prod`) y la deja en el config para que el humano la confirme en la
|
|
121
|
+
Etapa 1 — nunca se improvisa un casing distinto después de eso.
|
|
122
|
+
|
|
123
|
+
## A10 — Bindings de `project_iam` específicos de otro equipo/app
|
|
124
|
+
|
|
125
|
+
**Qué pasaba:** copiar usuarios de un dominio externo o un rol
|
|
126
|
+
personalizado que no existe en el proyecto nuevo.
|
|
127
|
+
**Resuelto en:** `build_component_project_iam` copia el archivo completo
|
|
128
|
+
(no hay forma mecánica confiable de distinguir "binding específico de la
|
|
129
|
+
app" de "binding que debería estar en todo entorno" sin entender el
|
|
130
|
+
negocio) pero **siempre** emite un `[WARN]` pidiendo revisión humana
|
|
131
|
+
explícita antes de confiar en el archivo. Este es, a propósito, el punto
|
|
132
|
+
donde este skill deja más criterio al agente/humano — es el mismo punto
|
|
133
|
+
donde la guía manual original tampoco pudo mecanizarlo del todo.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## B1 — `live/<NEW_ENV>/` nunca se crea antes de copiar `project.hcl`
|
|
138
|
+
|
|
139
|
+
**Qué pasaba (encontrado probando):** la primera versión de
|
|
140
|
+
`10_build_components.sh` fallaba en el primer paso (`cp: cannot create
|
|
141
|
+
regular file ... No such file or directory`) porque nunca se hacía
|
|
142
|
+
`mkdir -p` del directorio del entorno nuevo antes de copiar `project.hcl`
|
|
143
|
+
dentro de él.
|
|
144
|
+
**Resuelto en:** `mkdir -p "$NEW_DIR"` explícito justo después de
|
|
145
|
+
descubrir los componentes, antes de cualquier copia.
|
|
146
|
+
|
|
147
|
+
## B2 — Orden de construcción por alfabeto, no por dependencia real
|
|
148
|
+
|
|
149
|
+
**Qué pasaba (encontrado probando):** los componentes se descubren y
|
|
150
|
+
procesan alfabéticamente. `sql_private_access` necesita el rango CIDR que
|
|
151
|
+
calcula `vpc`, pero `"sql_private_access" < "vpc"` alfabéticamente, así
|
|
152
|
+
que se procesaba primero y fallaba por falta del archivo de CIDR.
|
|
153
|
+
**Resuelto en:** después de descubrir los componentes, si `vpc` está
|
|
154
|
+
presente se reordena al frente de la lista, antes que cualquier otro.
|
|
155
|
+
|
|
156
|
+
## B3 — Variable de shell reusada como variable de loop
|
|
157
|
+
|
|
158
|
+
**Qué pasaba (encontrado probando):** dentro de `build_component_vpc`, la
|
|
159
|
+
variable local `f` (la ruta del `vpc/terragrunt.hcl` nuevo) se declaraba
|
|
160
|
+
una vez, pero más abajo un `for f in .../sql_private_access/terragrunt.hcl`
|
|
161
|
+
reusaba el mismo nombre como variable de loop — al terminar el loop, `f`
|
|
162
|
+
quedaba apuntando al **último archivo de otro entorno** recorrido, no al
|
|
163
|
+
`vpc/terragrunt.hcl` que se quería editar. Las siguientes llamadas
|
|
164
|
+
editaban el archivo equivocado (de otro entorno ya desplegado).
|
|
165
|
+
**Resuelto en:** la variable de loop se renombró a `sp_file`, y se agregó
|
|
166
|
+
`local f` explícito en `ksc_copy_component_files` como defensa adicional.
|
|
167
|
+
**Por qué importa documentarlo:** si agregas lógica nueva a cualquier
|
|
168
|
+
`build_component_*`, nunca reuses el nombre de una variable `local` ya
|
|
169
|
+
declarada en la misma función como variable de loop — bash no lo detecta
|
|
170
|
+
ni avisa, simplemente sobreescribe en silencio.
|
|
171
|
+
|
|
172
|
+
## B4 — `sql_private_access.address` no lleva el prefijo CIDR
|
|
173
|
+
|
|
174
|
+
**Qué pasaba (encontrado probando):** el campo `address` de
|
|
175
|
+
`sql_private_access/terragrunt.hcl` es una IP **sin** sufijo `/NN` (el
|
|
176
|
+
prefijo va en un campo separado, `prefix_length`). El cálculo de rangos
|
|
177
|
+
nuevos producía strings `ipaddress.ip_network` completos
|
|
178
|
+
(`"10.0.0.0/16"`), y escribirlos tal cual en `address` genera un valor
|
|
179
|
+
inválido para ese módulo. Además, la recolección de rangos *existentes*
|
|
180
|
+
para evitar colisiones nunca leía estos `address`/`prefix_length`
|
|
181
|
+
separados — solo los CIDR completos de `vpc/terragrunt.hcl` — así que un
|
|
182
|
+
rango nuevo podía colisionar con un `sql_private_access` real sin que el
|
|
183
|
+
picker lo notara.
|
|
184
|
+
**Resuelto en:** `build_component_vpc` reconstruye el CIDR completo de
|
|
185
|
+
cada `sql_private_access/terragrunt.hcl` existente (`address` +
|
|
186
|
+
`prefix_length`) antes de buscar uno libre; `build_component_sql_private_access`
|
|
187
|
+
separa el rango elegido en `address` (sin sufijo) y `prefix_length` antes
|
|
188
|
+
de escribirlos.
|
|
189
|
+
|
|
190
|
+
## B5 — Archivos de nivel raíz de `live/<entorno>/` (no de un componente)
|
|
191
|
+
|
|
192
|
+
**Qué pasaba (encontrado probando, con `20_validate_file_parity.sh`):**
|
|
193
|
+
`boletera-qa` tiene un `Makefile` (corre `terragrunt run --all ...`) y un
|
|
194
|
+
`README.md` directamente en `live/boletera-qa/`, fuera de cualquier
|
|
195
|
+
componente. Nada en el proceso — ni este skill en su primera versión, ni
|
|
196
|
+
el proceso manual que generó `xoloshop-qa` en la sesión anterior — los
|
|
197
|
+
copiaba. El propio `xoloshop-qa` real de `infra-cloud` sigue sin estos dos
|
|
198
|
+
archivos hoy.
|
|
199
|
+
**Resuelto en:** paso `top-level-files` dedicado en
|
|
200
|
+
`10_build_components.sh`, antes del loop de componentes.
|
|
201
|
+
**Por qué importa:** es la prueba de por qué existe la Etapa 3 — el
|
|
202
|
+
proceso manual original nunca detectó esta falta en su momento porque
|
|
203
|
+
nadie comparó cantidades de archivo a conciencia; el script de validación
|
|
204
|
+
de paridad sí.
|
|
205
|
+
|
|
206
|
+
## B6 — Falso positivo en la validación de "project_id pinneado"
|
|
207
|
+
|
|
208
|
+
**Qué pasaba (encontrado probando):** la primera versión de
|
|
209
|
+
`21_validate_project_pinning.sh` exigía que **todo** componente tuviera
|
|
210
|
+
una clave `inputs.project_id = local.project_id`. Pero `org_policies` usa
|
|
211
|
+
`parent = "projects/${local.project_id}"` y
|
|
212
|
+
`storage_assets`/`storage_docs` usan `project = local.project_id` — nunca
|
|
213
|
+
una clave llamada `project_id` en `inputs`. El check marcaba estos 3
|
|
214
|
+
componentes como fallidos aunque estaban perfectamente bien pinneados.
|
|
215
|
+
**Resuelto en:** el check se generalizó a "¿aparece la substring exacta
|
|
216
|
+
`local.project_id` en algún lado del archivo?" — cubre cualquier forma en
|
|
217
|
+
que un módulo use el project id, sin asumir un nombre de clave fijo.
|
|
218
|
+
|
|
219
|
+
## B7 — `dns_record_name`/`dns_domain_name` no son un patrón de texto genérico
|
|
220
|
+
|
|
221
|
+
**Qué pasaba (encontrado probando):** `sql_postgres` de varios entornos
|
|
222
|
+
(no `xoloshop-qa`, que nunca los configuró) tiene
|
|
223
|
+
`dns_record_name = "boletera.db"` y
|
|
224
|
+
`dns_domain_name = "qa.boletera.ninja."`. A simple vista parece el mismo
|
|
225
|
+
patrón `<app>.algo` que el resto de los campos, pero en realidad codifica
|
|
226
|
+
un **dominio DNS real que la organización tiene registrado para esa app
|
|
227
|
+
específica** (`boletera.ninja`, `cashless.ninja`) — no hay garantía de que
|
|
228
|
+
la app nueva vaya a vivir bajo `<NEW_ENV_APP>.ninja`.
|
|
229
|
+
**Resuelto en:** `build_component_sql_postgres` genera un valor por el
|
|
230
|
+
mismo patrón visual **solo si el campo ya existía en `SOURCE_ENV`**, pero
|
|
231
|
+
**siempre** lo marca con un `[WARN]` pidiendo confirmación humana — a
|
|
232
|
+
diferencia de los demás campos (nombres de red, SA, CIDR), donde la
|
|
233
|
+
sustitución mecánica sí es segura porque no representa una decisión de
|
|
234
|
+
negocio externa al repo.
|
|
235
|
+
|
|
236
|
+
## B8 — `github_workload_identity` puede manejar varios repos de la misma app
|
|
237
|
+
|
|
238
|
+
**Qué pasaba (encontrado probando):** se asumió que este componente
|
|
239
|
+
siempre tiene exactamente 2 bindings (`terraform` + un `github-manager`).
|
|
240
|
+
`boletera-qa` real tiene **6**: el de `terraform` y 5 bindings
|
|
241
|
+
`github-manager` distintos, uno por cada repo de su propia arquitectura
|
|
242
|
+
multi-repo (`xbol-web-admin`, `xbol-api-client`, etc.). La primera versión
|
|
243
|
+
de este skill solo corregía el *segundo* binding encontrado y dejaba los
|
|
244
|
+
otros 4 con nombres de repo obsoletos de `SOURCE_ENV`, sin avisar.
|
|
245
|
+
**Resuelto en (versión original):** contar los bindings; si eran
|
|
246
|
+
exactamente 2 (caso simple) ajustar automáticamente, y si eran más, no
|
|
247
|
+
adivinar — solo `[WARN]`. **Superado por C6** (ver sección C): ahora el
|
|
248
|
+
bloque completo se reconstruye siempre, soportando una lista de repos en
|
|
249
|
+
`APP_GITHUB_REPO` — ya no hace falta elegir entre "caso simple" y
|
|
250
|
+
"advertir sin tocar".
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## C1 — La construcción moría en el reintento (reanudación prometida pero rota)
|
|
255
|
+
|
|
256
|
+
**Qué pasaba:** `mkdir -p "$NEW_DIR"` corría antes que cualquier
|
|
257
|
+
componente, así que un fallo posterior dejaba `live/<NEW_ENV>/` creado —
|
|
258
|
+
y una guarda previa (`[[ ! -d "$NEW_DIR" ]] || ksc_die ...`) trataba eso
|
|
259
|
+
como "ya existe, no tocar" en el siguiente intento. La reanudación que
|
|
260
|
+
`SKILL.md`/`IDEA.md` prometían no existía en la práctica.
|
|
261
|
+
**Resuelto en:** se quitó la guarda. `10_build_components.sh` ahora
|
|
262
|
+
reconstruye cada componente siempre desde `SOURCE_DIR` (nunca incrementa
|
|
263
|
+
sobre `NEW_DIR`), así que el reintento es naturalmente idempotente — no
|
|
264
|
+
hace falta un motor de reanudación por paso, solo que nada dependa de un
|
|
265
|
+
estado parcial previo.
|
|
266
|
+
|
|
267
|
+
## C2 — El picker de CIDR podía asignar un rango que se solapa consigo mismo
|
|
268
|
+
|
|
269
|
+
**Qué pasaba:** la fórmula probaba un solo `N` para los 4 bloques
|
|
270
|
+
(subred, pods, svc, `sql_private_access`) y solo los validaba contra los
|
|
271
|
+
rangos **ya existentes**, nunca entre sí. Cuando `N` resultaba múltiplo de
|
|
272
|
+
10, la subred (`10.N.0.0/22`) quedaba **contenida** en su propio
|
|
273
|
+
`sql_private_access` (`10.N.0.0/16`) — un solapamiento real que solo se
|
|
274
|
+
detecta desplegando. Reproducido: el primer rango libre daba `N=1`
|
|
275
|
+
(válido), pero el segundo consecutivo daba `N=10` (inválido).
|
|
276
|
+
**Resuelto en:** `ksc_cidr_pick_free` ahora busca por década
|
|
277
|
+
(`sql_private_access = 10.<década>.0.0/16`) y, dentro de cada década, por
|
|
278
|
+
un offset ≥ 1 para la subred (preferido `+3`, la convención real
|
|
279
|
+
dominante del repo) — la subred nunca puede caer en la década exacta de
|
|
280
|
+
su propio `/16` por construcción, no solo por un chequeo adicional (que
|
|
281
|
+
también se agregó).
|
|
282
|
+
|
|
283
|
+
## C3 — `boletera-qa` (el ejemplo del propio README) rompía el build
|
|
284
|
+
|
|
285
|
+
**Qué pasaba:** `build_top_level_files` fallaba la construcción completa
|
|
286
|
+
si `SOURCE_ENV` no tenía alguno de los 3 archivos de nivel raíz
|
|
287
|
+
(`Makefile`/`README.md`/`.gitignore`) — el caso real de `xoloshop-qa`,
|
|
288
|
+
que es justo el entorno más "sano" del repo (el único con
|
|
289
|
+
`orgpolicy.googleapis.com` y las 6 `org_policies`).
|
|
290
|
+
**Resuelto en:** copiar cada archivo de nivel raíz es ahora
|
|
291
|
+
condicional-sin-fallar (`if [[ -f ... ]]; then cp ...; fi`, nunca un `&&`
|
|
292
|
+
encadenado cuyo valor de verdad se propague como el de toda la función) —
|
|
293
|
+
que falte uno de los 3 es normal, no un error.
|
|
294
|
+
|
|
295
|
+
## C4 — Project_iam/org_policies/project_services de `SOURCE_ENV` se copiaban sin verificar que estuvieran sanos
|
|
296
|
+
|
|
297
|
+
**Qué pasaba:** la estrategia "copiar tal cual" de A2/A7 asume
|
|
298
|
+
implícitamente que `SOURCE_ENV` ya tiene todo lo que un entorno nuevo
|
|
299
|
+
necesita. En la práctica, 4 de los 5 entornos reales de este repo NO
|
|
300
|
+
tienen `orgpolicy.googleapis.com` ni las 6 `org_policies` — construir
|
|
301
|
+
desde cualquiera de ellos (incluido `boletera-qa`, el ejemplo del README)
|
|
302
|
+
reproduce en silencio exactamente el error #7 de
|
|
303
|
+
`CreateScriptsNewGCPProjectInstructions.md`.
|
|
304
|
+
**Resuelto en:** `ksc_check_source_health` (nueva, en `lib/common.sh`,
|
|
305
|
+
invocada desde `02_print_config.sh` antes de confirmar) compara
|
|
306
|
+
`SOURCE_ENV` contra el resto de `live/` y advierte si le falta una API o
|
|
307
|
+
le faltan entradas de `org_policies` que **el entorno modificado más
|
|
308
|
+
recientemente** ya tiene. Se usa "el más reciente" y no "la mayoría"
|
|
309
|
+
porque un requisito nuevo de la organización, por definición, todavía NO
|
|
310
|
+
lo tiene la mayoría de entornos viejos — una votación por mayoría nunca
|
|
311
|
+
lo habría detectado (se probó: con mayoría, el hueco real de
|
|
312
|
+
`boletera-qa` no se veía; con "más reciente" sí). La misma función
|
|
313
|
+
también advierte si la IP fija de `cloudadmin` de `SOURCE_ENV` difiere de
|
|
314
|
+
la mayoría del resto de `live/` (ahí sí conviene mayoría, no "más
|
|
315
|
+
reciente" — una IP de oficina compartida rara vez cambia legítimamente).
|
|
316
|
+
Nunca bloquea nada — solo advierte, porque un `SOURCE_ENV` minimalista a
|
|
317
|
+
propósito es una posibilidad legítima que el humano puede confirmar.
|
|
318
|
+
|
|
319
|
+
## C5 — Sustitución incompleta del nombre de la app fuera de `artifact_registry`
|
|
320
|
+
|
|
321
|
+
**Qué pasaba:** solo se sustituía el token completo del entorno
|
|
322
|
+
(`boletera-qa` → `shopdemo-qa`), nunca el nombre corto de la app
|
|
323
|
+
(`boletera`) ni su capitalizado (`Boletera`), salvo en
|
|
324
|
+
`artifact_registry`. Resultado real: `service_accounts` dejaba
|
|
325
|
+
`display_name = "Boletera QA Node Service Account"` y `project_iam`
|
|
326
|
+
dejaba namespaces/bindings con `boletera` sin tocar.
|
|
327
|
+
**Resuelto en:** `ksc_copy_component_files` (la función que TODO
|
|
328
|
+
`build_component_*` usa para copiar) ahora aplica, para cualquier
|
|
329
|
+
componente, tres sustituciones en orden: el token completo del entorno,
|
|
330
|
+
el nombre corto de la app en minúscula, y el nombre corto capitalizado —
|
|
331
|
+
una sola vez, en un solo lugar, en vez de que cada componente decidiera
|
|
332
|
+
por su cuenta cuáles aplicar (y se le olvidara a varios).
|
|
333
|
+
|
|
334
|
+
## C6 — Editar `github_workload_identity` por posición era frágil, y no soportaba apps multi-repo
|
|
335
|
+
|
|
336
|
+
**Qué pasaba:** el binding de `github-manager` se identificaba por ser
|
|
337
|
+
la "2.ª coincidencia" de `principal_set_suffix` en el archivo — si
|
|
338
|
+
`SOURCE_ENV` tuviera los 2 bindings en el orden inverso, se habría
|
|
339
|
+
corrompido el binding de `terraform`/`infra-cloud` en vez del de la app.
|
|
340
|
+
Además, cuando `SOURCE_ENV` tenía más de 2 bindings (apps multi-repo como
|
|
341
|
+
`boletera-qa`, con 5), el skill solo advertía y dejaba los 5 nombres de
|
|
342
|
+
repo viejos sin tocar.
|
|
343
|
+
**Resuelto en:** `ksc_set_workload_identity_bindings` reconstruye el
|
|
344
|
+
bloque completo: ubica el binding de `terraform` **por contenido**
|
|
345
|
+
(`service_account_email` que empieza con `"terraform@"`, nunca por
|
|
346
|
+
posición) y genera un binding `github-manager` por cada repo listado en
|
|
347
|
+
`APP_GITHUB_REPO` (separados por `;`) — soporta tanto el caso simple (1
|
|
348
|
+
repo) como el multi-repo, sin advertir-y-no-tocar.
|
|
349
|
+
|
|
350
|
+
## C6b — No todo entorno real tiene un binding de terraform en `github_workload_identity`
|
|
351
|
+
|
|
352
|
+
**Qué pasaba (encontrado verificando el fix de C6 con una CUARTA fuente,
|
|
353
|
+
`cashless-qa`, que ninguna ronda anterior de pruebas había usado):**
|
|
354
|
+
`cashless-qa` real solo tiene un binding `github-manager` — nunca tuvo
|
|
355
|
+
uno de `terraform`. `ksc_set_workload_identity_bindings` (y la extracción
|
|
356
|
+
previa en `build_component_github_workload_identity`) asumían que ese
|
|
357
|
+
binding siempre existe y fallaban la construcción completa si no lo
|
|
358
|
+
encontraban.
|
|
359
|
+
**Resuelto en:** si no hay binding de `terraform@...`, no es un error —
|
|
360
|
+
se usa `"infra-cloud"` como `principal_set_suffix` por defecto (el valor
|
|
361
|
+
que la guía §3.15 pide siempre) y se avisa con `[WARN]`, en vez de morir.
|
|
362
|
+
|
|
363
|
+
## C7 — Lecciones menores de la misma revisión
|
|
364
|
+
|
|
365
|
+
- **Componentes opcionales copiados siempre:** el skill copiaba *todos*
|
|
366
|
+
los componentes que `SOURCE_ENV` tuviera, incluyendo los que la guía
|
|
367
|
+
(§1.2) pide explícitamente no incluir por defecto
|
|
368
|
+
(`cloud_armor`/`identity_platform`/`monitoring_alerts`/`secrets`).
|
|
369
|
+
Resuelto con `ksc_base_components` (intersección real de componentes en
|
|
370
|
+
TODOS los entornos de `live/`, sin lista hardcodeada — y excluyendo
|
|
371
|
+
explícitamente cualquier entorno que no siga el patrón `<app>-qa|prod>`,
|
|
372
|
+
como un proyecto de infraestructura compartida) + el campo
|
|
373
|
+
`EXTRA_COMPONENTS` del config para pedirlos de vuelta explícitamente.
|
|
374
|
+
- **Tracking como dependencia dura:** si `github-proyect-tracking` no
|
|
375
|
+
podía inicializar (sin red, token, URL inválida), toda la construcción
|
|
376
|
+
moría — aunque es 100% local. Resuelto: el tracking es ahora
|
|
377
|
+
best-effort (`[WARN]` + seguir sin reanudación remota).
|
|
378
|
+
- **`deletion_protection` en `*-prod`:** se copiaba de `SOURCE_ENV` sin
|
|
379
|
+
aviso; si `SOURCE_ENV` es qa (`"false"`) y `NEW_ENV` es prod, quedaba
|
|
380
|
+
`"false"` en prod en silencio. Resuelto con un `[WARN]` específico.
|
|
381
|
+
- **Clave HCL duplicada heredada de `SOURCE_ENV`** (ej.
|
|
382
|
+
`boletera-qa/sql_postgres` trae `update_timeout` dos veces dentro del
|
|
383
|
+
mismo bloque `inputs`): ni `terragrunt hcl fmt` ni `hcl validate` lo
|
|
384
|
+
detectan. Resuelto con `ksc_check_duplicate_keys`, corrido al final de
|
|
385
|
+
la construcción sobre cada componente (cuidado: tiene que ser
|
|
386
|
+
consciente de bloques `{ }` — una primera versión confundía
|
|
387
|
+
`locals.project_id` con `inputs.project_id` como "duplicado" por tener
|
|
388
|
+
la misma indentación).
|
|
389
|
+
- **Dos falsos positivos de los propios chequeos de este skill, encontrados
|
|
390
|
+
recién al probar con `xoloshop-qa` como fuente** (no con `boletera-qa`):
|
|
391
|
+
el comentario que el propio skill agrega en `project_iam` explicando por
|
|
392
|
+
qué no se puede usar `roles/orgpolicy.policyAdmin` ahí contiene ese
|
|
393
|
+
mismo string textualmente, y el chequeo de "¿quedó algo de SOURCE_ENV
|
|
394
|
+
sin sustituir?" no distinguía comentarios de código real. Ambos
|
|
395
|
+
chequeos ahora ignoran líneas que empiezan con `#`.
|
|
396
|
+
- **`terragrunt hcl validate` por componente** (guía §3.17) no se corría,
|
|
397
|
+
solo `hcl fmt`. Se agregó al final de la construcción, tolerando el
|
|
398
|
+
falso positivo conocido de `gke_private`.
|
|
399
|
+
- Bug de texto: un carácter de guion suave invisible (U+00AD) dentro de
|
|
400
|
+
"realmente" en un mensaje `[WARN]`, y la plantilla del config decía
|
|
401
|
+
"separadas por coma" cuando el formato real usa `;`. Ambos corregidos.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## D — Encontradas por `Review_V2.md` (re-revisión después de aplicar C)
|
|
406
|
+
|
|
407
|
+
`Review_V2.md` volvió a ejecutar el skill de punta a punta contra las
|
|
408
|
+
correcciones de la sección C, con varias fuentes y, de forma crítica,
|
|
409
|
+
con reintentos reales sobre entornos a medio construir. Encontró un bug
|
|
410
|
+
crítico nuevo — una regresión directa de C1 — y varios menores.
|
|
411
|
+
|
|
412
|
+
## D1 (N-1) — CRÍTICO: la base de componentes se contaminaba con entornos sin comitear
|
|
413
|
+
|
|
414
|
+
**Qué pasaba:** `ksc_base_components` contaba cualquier directorio de
|
|
415
|
+
`live/` que pareciera un entorno (`^[a-z0-9]+-(qa|prod)$`) — incluido un
|
|
416
|
+
`live/<NEW_ENV>/` a medio construir (el estado normal de un reintento
|
|
417
|
+
desde C1) y, peor, cualquier OTRO entorno abandonado de una corrida
|
|
418
|
+
anterior, con cualquier nombre. Reproducido en cadena: un entorno
|
|
419
|
+
mutilado a 2 componentes hacía que un entorno nuevo e inocente **también**
|
|
420
|
+
construyera solo 2 componentes, con `FILE_PARITY_OK`/`PROJECT_PINNING_OK`
|
|
421
|
+
en verde — exactamente el defecto silencioso que este skill existe para
|
|
422
|
+
eliminar.
|
|
423
|
+
**Resuelto en:** `ksc_real_envs` (nueva, consolidada): un entorno solo
|
|
424
|
+
cuenta si además de cumplir el patrón de nombre tiene **al menos un
|
|
425
|
+
archivo comiteado en git** bajo su `live/<env>/`. Este skill nunca
|
|
426
|
+
comitea nada, así que cualquier intento (parcial o completo, propio o de
|
|
427
|
+
otro `NEW_ENV`) queda excluido automáticamente, sin tener que conocer su
|
|
428
|
+
nombre ni asumir que "bien formado" significa "completo". Se usa en
|
|
429
|
+
`ksc_base_components`, `ksc_check_source_health` y
|
|
430
|
+
`ksc_majority_fixed_network` — las tres tenían el mismo filtro
|
|
431
|
+
incompleto (D2).
|
|
432
|
+
|
|
433
|
+
## D2 — El mismo filtro incompleto estaba duplicado en 3 funciones
|
|
434
|
+
|
|
435
|
+
**Qué pasaba (encontrado aplicando el fix de D1):** el filtro
|
|
436
|
+
"solo entornos reales" estaba copiado a mano en `ksc_base_components`,
|
|
437
|
+
`ksc_check_source_health` y `ksc_majority_fixed_network` por separado.
|
|
438
|
+
Corregirlo solo en la primera (como un primer intento de fix hizo)
|
|
439
|
+
dejaba las otras dos igual de contaminables — confirmado probando: con
|
|
440
|
+
dos entornos de prueba sin comitear en `live/`, el doctor reportaba "6/7
|
|
441
|
+
entornos reales" en vez de "4/5".
|
|
442
|
+
**Resuelto en:** las tres ahora llaman a `ksc_real_envs`, una sola
|
|
443
|
+
fuente de verdad. Lección operativa: cuando un fix corrige una función,
|
|
444
|
+
`grep` el resto del archivo por lógica duplicada antes de dar el fix por
|
|
445
|
+
completo.
|
|
446
|
+
|
|
447
|
+
## D3 — Bug de `set -e` introducido por el propio fix de N-6
|
|
448
|
+
|
|
449
|
+
**Qué pasaba (encontrado probando el fix, antes de llegar a reportarlo):**
|
|
450
|
+
el código nuevo para preferir la IP mayoritaria (ver D5) usaba
|
|
451
|
+
`[[ condición ]] && variable=valor` como declaración suelta dentro de un
|
|
452
|
+
`for`, en un script con `set -e`. Cuando la condición daba falso para
|
|
453
|
+
**cualquier** entorno (ej. `live/shared/`, que no matchea
|
|
454
|
+
`<app>-qa|prod>`), el script completo moría ahí mismo — y como venía
|
|
455
|
+
ANTES del bloque que autodetecta `ORG_ID`/`GKE_VERSION`/etc., esos
|
|
456
|
+
campos quedaban `__PENDIENTE__` sin ninguna pista de por qué.
|
|
457
|
+
**Resuelto:** `if`/`fi` en vez de `&&` para cualquier asignación con
|
|
458
|
+
efecto secundario que dependa de una condición que puede ser falsa,
|
|
459
|
+
dentro de un script con `set -e`. Lección operativa: `A && B` es seguro
|
|
460
|
+
bajo `set -e` si B puede fallar (A fallar está exento), pero mezclar
|
|
461
|
+
asignaciones de esta forma invita a confundirse — mejor `if` siempre que
|
|
462
|
+
el cuerpo no sea un one-liner trivial como `continue`.
|
|
463
|
+
|
|
464
|
+
## D4 (N-2) — `01_init_config.sh` moría en un rescate legítimo
|
|
465
|
+
|
|
466
|
+
**Qué pasaba:** después del fix de reanudación de C1, `10` ya no
|
|
467
|
+
fallaba por que `live/<NEW_ENV>/` existiera — pero `01` sí, con un
|
|
468
|
+
mensaje que literalmente decía "bórralo tú mismo primero", contradiciendo
|
|
469
|
+
la instrucción de `SKILL.md` de simplemente re-correr `10`.
|
|
470
|
+
**Resuelto en:** `01` ahora distingue "`live/<NEW_ENV>/` existe y el
|
|
471
|
+
config de este skill YA existe" (reintento legítimo, no es error) de
|
|
472
|
+
"existe pero no hay config" (colisión real con otra cosa, sí es error).
|
|
473
|
+
|
|
474
|
+
## D5 (N-6) — El doctor avisaba del typo pero el config igual escribía el valor malo
|
|
475
|
+
|
|
476
|
+
**Qué pasaba:** `ksc_check_source_health` ya sabía que la mayoría de
|
|
477
|
+
`live/` usa `177.226.232.136/32` para `cloudadmin` y que `xoloshop-qa`
|
|
478
|
+
tiene un valor distinto — pero solo avisaba; `01_init_config.sh` seguía
|
|
479
|
+
autodetectando y escribiendo el valor de `SOURCE_ENV` tal cual.
|
|
480
|
+
**Resuelto en:** `ksc_majority_fixed_network` (extraída del doctor,
|
|
481
|
+
reusada) + lógica en `01_init_config.sh` que adopta el valor mayoritario
|
|
482
|
+
en el config cuando hay una mayoría clara (>mitad de los entornos
|
|
483
|
+
reales) y difiere del de `SOURCE_ENV`, con un `[WARN]` explicando el
|
|
484
|
+
cambio (no en silencio).
|
|
485
|
+
|
|
486
|
+
## D6 (N-7) — El picker de CIDR preferiría la década 0 o rellenaría huecos bajos
|
|
487
|
+
|
|
488
|
+
**Qué pasaba:** el picker probaba décadas en orden `0, 10, 20, ...` —
|
|
489
|
+
técnicamente sin colisión, pero `10.0.x.x` se reserva convencionalmente
|
|
490
|
+
(VPN/defaults/on-prem) y rellenar huecos bajos (`10, 50, ...`) en vez de
|
|
491
|
+
seguir la secuencia real (`20, 30, 40, 130, 140`) es menos predecible
|
|
492
|
+
para quien revisa el PR.
|
|
493
|
+
**Resuelto en:** el orden de búsqueda ahora prioriza la primera década
|
|
494
|
+
libre **por encima** de la mayor ya usada, y la década 0 queda como
|
|
495
|
+
último recurso, no primera opción.
|
|
496
|
+
|
|
497
|
+
**Corrección posterior (Review V3, P-4):** la primera versión de esta
|
|
498
|
+
lección decía "el siguiente lógico es 150" — eso es matemáticamente
|
|
499
|
+
imposible con la fórmula real: `svc = 10.<década+off+106>.0.0/23`
|
|
500
|
+
necesita `década+off ≤ 149` para que el segundo octeto no pase de 255,
|
|
501
|
+
lo que limita la década máxima usable a **140** sin importar el offset.
|
|
502
|
+
El código ya se comportaba bien (`candidate()` devuelve `None` para
|
|
503
|
+
cualquier década inválida y el loop sigue), pero probaba décadas hasta
|
|
504
|
+
240 sin necesidad; se acotó el rango generado a `0..140` para que el
|
|
505
|
+
código y el comentario coincidan con el techo real de la fórmula.
|
|
506
|
+
|
|
507
|
+
## D7 — Mejora pendiente resuelta: `artifact_registry` determinista sin hardcodear la organización
|
|
508
|
+
|
|
509
|
+
Encontrada como mejora abierta en la propia `Review_V2.md` (no un bug):
|
|
510
|
+
cuando `SOURCE_ENV` tiene `repository_id`/`description` genéricos (4 de
|
|
511
|
+
5 entornos reales), en vez de solo advertir, `ksc_learn_artifact_registry_template`
|
|
512
|
+
busca OTRO entorno de `live/` que sí tenga su propio nombre de app en su
|
|
513
|
+
`artifact_registry`, extrae el patrón reemplazando ese nombre por un
|
|
514
|
+
marcador, y lo aplica con el nombre de la app nueva. Nunca se hardcodea
|
|
515
|
+
"club-tijuana" ni ningún otro literal de la organización en el código
|
|
516
|
+
del skill — se aprende de los datos reales de `live/`, igual que el
|
|
517
|
+
doctor.
|
|
518
|
+
|
|
519
|
+
## D8 — Lecciones menores de la misma re-revisión
|
|
520
|
+
|
|
521
|
+
- **Cobertura del leftover-check reducida (N-3):** V1 revisaba todos los
|
|
522
|
+
archivos de `live/<NEW_ENV>/`; el fix de ignorar comentarios (B-algo)
|
|
523
|
+
de la ronda anterior redujo por accidente la lista a solo
|
|
524
|
+
`terragrunt.hcl`/`project.hcl`/`README.md`, perdiendo
|
|
525
|
+
`Makefile`/`.gitignore` de cada componente. Restaurado con `find` sobre
|
|
526
|
+
todo el árbol (2 niveles), filtrando comentarios línea por línea en vez
|
|
527
|
+
de limitar por nombre de archivo.
|
|
528
|
+
- **WARNs del doctor no sobrevivían a la Etapa 1 (N-5):** `KSC_WARNINGS_FILE`
|
|
529
|
+
solo se fijaba en `10_build_components.sh`; si la sesión moría entre
|
|
530
|
+
`02` y `10`, los avisos del doctor se perdían. Ahora `02` también la fija
|
|
531
|
+
(truncando, fresca cada corrida) y `10` hace *append* en vez de truncar.
|
|
532
|
+
- **Código muerto (N-4):** una variable (`DUPLICATE_ISSUES`) que se
|
|
533
|
+
asignaba y nunca se leía. Eliminada.
|
|
534
|
+
- **`SKILL.md` no cubría `finalize` sin tracking (N-8):** si el tracking
|
|
535
|
+
nunca se inicializó, la instrucción de cerrar con `run_tracking.sh
|
|
536
|
+
finalize` fallaría sin explicación. Se agregó la condición explícita.
|
|
537
|
+
- **Cosmético (N-9):** `uninstall.sh --clean-gitignore` dejaba una línea
|
|
538
|
+
en blanco suelta al final del `.gitignore`.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## E1 (P-1) — La función nueva de D7 nació con la misma contaminación que D1/D2 ya habían corregido
|
|
543
|
+
|
|
544
|
+
**Qué pasaba:** `ksc_learn_artifact_registry_template` (agregada en la
|
|
545
|
+
misma ronda que D7) iteraba `live/*/` directamente, sin pasar por
|
|
546
|
+
`ksc_real_envs` — exactamente el filtro incompleto que D1/D2 ya habían
|
|
547
|
+
consolidado en las otras tres funciones. Reproducido: un entorno sin
|
|
548
|
+
comitear (`live/aaaajunk-qa/`) con `repository_id =
|
|
549
|
+
"aaaajunk-mega-custom-repo"` se "aprendía" como si fuera una plantilla
|
|
550
|
+
real, con un `[OK]` que además afirmaba (incorrectamente) que era un
|
|
551
|
+
"entorno real".
|
|
552
|
+
**Resuelto en:** la función ahora usa `ksc_real_envs` como las demás, y
|
|
553
|
+
de paso adoptó el mismo criterio de "preferir el más reciente" del
|
|
554
|
+
doctor en vez de "el primero alfabético" — dos entornos específicos con
|
|
555
|
+
nombres distintos ya no dependen de en qué orden los liste el
|
|
556
|
+
filesystem.
|
|
557
|
+
**Lección operativa, otra vez:** cada función nueva que itere `live/*/`
|
|
558
|
+
tiene que pasar por `ksc_real_envs` desde el día uno — no basta con
|
|
559
|
+
haberlo corregido en las funciones que ya existían (D2 ya dejó esta
|
|
560
|
+
lección una vez; aquí se repitió con una función que no existía
|
|
561
|
+
todavía cuando D2 se escribió).
|
|
562
|
+
|
|
563
|
+
## E2 (P-2) — Rescate imposible si se perdía el config
|
|
564
|
+
|
|
565
|
+
**Qué pasaba:** si `.kcreate-scaffolding/<env>.config` se perdía (ej.
|
|
566
|
+
por `uninstall.sh --clean-gitignore`, que la borra a propósito) con
|
|
567
|
+
`live/<NEW_ENV>/` todavía a medias, `01_init_config.sh` trataba cualquier
|
|
568
|
+
`live/<NEW_ENV>/` sin config como colisión real y recomendaba borrar
|
|
569
|
+
exactamente lo que se quería rescatar — sin camino automático de vuelta.
|
|
570
|
+
**Resuelto en:** `01` ahora reconoce una firma mínima
|
|
571
|
+
(`project.hcl` con `project_id` == `NEW_ENV`, que `build_project_hcl`
|
|
572
|
+
siempre deja) para distinguir "rescate legítimo" de "colisión real", y
|
|
573
|
+
regenera el config desde cero en el primer caso.
|
|
574
|
+
|
|
575
|
+
## E3 (P-3) — Los warnings de la Construcción se duplicaban en cada reintento
|
|
576
|
+
|
|
577
|
+
**Qué pasaba:** el fix de persistencia de D-faltante-17/N-5 usó un solo
|
|
578
|
+
archivo en modo *append* para doctor + build. Correcto para que el
|
|
579
|
+
doctor sobreviva entre `02` y `10`, pero cada reintento de `10` volvía a
|
|
580
|
+
escribir sus propios WARN sobre los de la corrida anterior — un archivo
|
|
581
|
+
que pasaba de 8 a 14 líneas con un solo retry, sin límite.
|
|
582
|
+
**Resuelto en:** dos archivos separados
|
|
583
|
+
(`ksc_warnings_file_path` para el doctor, `ksc_build_warnings_file_path`
|
|
584
|
+
para la construcción). El del doctor se trunca en cada `02` (sigue
|
|
585
|
+
fresco); el de la construcción se trunca al INICIO de cada `10` (no
|
|
586
|
+
repite sus propios WARN en un retry). El reporte final combina ambos.
|
|
587
|
+
|
|
588
|
+
## E4 (P-4) — Comentario/lección describía un comportamiento matemáticamente imposible
|
|
589
|
+
|
|
590
|
+
**Qué pasaba:** D6 (y el comentario del código) decían que el picker de
|
|
591
|
+
CIDR, al no encontrar una década libre cerca, seguiría hasta "150, 160,
|
|
592
|
+
...". La fórmula real (`svc = 10.<década+off+106>.0.0/23`) hace que
|
|
593
|
+
cualquier década ≥ 150 exceda 255 en el segundo octeto para todo
|
|
594
|
+
offset — esas décadas nunca se usan, sin importar qué tan lejos llegue
|
|
595
|
+
el rango de búsqueda. El código ya se comportaba bien (`candidate()`
|
|
596
|
+
devuelve `None` y el loop sigue), solo el comentario/lección prometían
|
|
597
|
+
algo que no podía pasar.
|
|
598
|
+
**Resuelto en:** el rango de búsqueda ahora se acota a `0..140` (el
|
|
599
|
+
techo real de la fórmula) y el comentario/D6 se corrigieron para
|
|
600
|
+
describir el comportamiento real, no uno aspiracional.
|
|
601
|
+
|
|
602
|
+
## E5 — Observación: repo destino sin ningún commit
|
|
603
|
+
|
|
604
|
+
Si `live/` no tiene NINGÚN entorno comiteado (un repo de infra recién
|
|
605
|
+
creado, sin commit inicial), `ksc_real_envs` da una lista vacía y la
|
|
606
|
+
base de componentes también queda vacía — el build excluiría todo salvo
|
|
607
|
+
lo que `EXTRA_COMPONENTS` pida. No ocurre en un repo de infra real (que
|
|
608
|
+
siempre tiene historia), pero se agregó un `[WARN]` explícito en
|
|
609
|
+
`ksc_base_components` para ese caso, en vez de dejar que "todo se
|
|
610
|
+
excluye" pase sin ninguna pista de la causa.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## Qué significa esta lista para quien edite este skill después
|
|
615
|
+
|
|
616
|
+
Cada vez que se agregue un `build_component_<algo>` nuevo o se modifique
|
|
617
|
+
uno existente: **pruébalo contra una copia descartable de un repo real**
|
|
618
|
+
(nunca contra el repo de trabajo del usuario) antes de considerarlo
|
|
619
|
+
terminado, igual que se hizo para construir las lecciones B1-B8. La mitad
|
|
620
|
+
de los bugs reales de este skill nunca se habrían notado solo leyendo el
|
|
621
|
+
código — se encontraron ejecutándolo de punta a punta contra datos reales
|
|
622
|
+
y revisando el resultado campo por campo, no solo confiando en que los
|
|
623
|
+
checks automatizados pasaran.
|