Módulo 8: Capstone The Andes Cargo Reliability Package
5. Recorrido end-to-end: el runbook mitiga, el postmortem cierra
Descripción
El incidente ya está declarado —SEV1, roles asignados, la alerta confirmada en tres motores—. Esta lección hace lo que un Operations Lead real haría a continuación: seguir runbooks/manifest-processor-error-rate.md (Módulo 7, lección 6), paso a paso, sin modificar ni una línea, contra los datos reales de este incidente. La primera vez que ese runbook se ejecutó, en el Módulo 7, cayó en la Rama B (ruido disperso, sin acción de código). Esta es la primera vez que alguien lo ejecuta y cae en la Rama A —la que el Módulo 7 nunca llegó a demostrar con un caso real—. Cuando la alarma vuelve a OK, esta lección cierra con un segundo POSTMORTEM.md, más corto que el del incidente Claude Code, pero con la misma disciplina sin culpa completa.
Conexión con el módulo
Cada comando de esta lección es literalmente el mismo comando de runbooks/manifest-processor-error-rate.md, con los datos de la lección 3 y 4 de este módulo en vez de los datos del Módulo 3 que el runbook usó como ejemplo original. El segundo POSTMORTEM.md de esta lección sigue la misma plantilla de Google SRE que el Módulo 7, lección 3 ya aplicó al incidente Claude Code —resumen, impacto, causa raíz distinguida de disparador, qué salió bien/mal, action items—, pero deliberadamente más corto: menos causas raíz, porque el incidente en sí es más simple, no porque la disciplina sea menos rigurosa.
Paso 1 — Corriendo el runbook, paso a paso, contra este incidente
Runbook Step 1 — Confirm the alarm actually fired.
awslocal cloudwatch describe-alarms \
--alarm-names andes-cargo-manifest-error-budget-burn-rate \
--query 'MetricAlarms[0].{State:StateValue,Reason:StateReason}'
Qué esperar (representativo — el mismo resultado ya confirmado en la lección 4, Paso 3, motor 3 de este módulo):
{
"State": "ALARM",
"Reason": "Threshold Crossed: 1 datapoint [0.025 (17/03/26 15:00:00)] was greater than or equal to the threshold (0.001)."
}
State: ALARM — el runbook continúa al Paso 2. Si esta consulta hubiera devuelto OK, el runbook mismo indica detenerse aquí; no es el caso.
Runbook Step 2 — Pull the raw numbers behind the ratio.
awslocal cloudwatch get-metric-statistics \
--namespace AWS/Lambda --metric-name Invocations \
--dimensions Name=FunctionName,Value=process-shipment-manifest \
--start-time 2026-03-17T15:00:00Z --end-time 2026-03-17T16:00:00Z \
--period 3600 --statistics Sum
awslocal cloudwatch get-metric-statistics \
--namespace AWS/Lambda --metric-name Errors \
--dimensions Name=FunctionName,Value=process-shipment-manifest \
--start-time 2026-03-17T15:00:00Z --end-time 2026-03-17T16:00:00Z \
--period 3600 --statistics Sum
Qué esperar (representativo, misma razón que el Paso 1; los mismos números de la lección 3 de este módulo, ventana larga):
Invocations (Sum): 400.0
Errors (Sum): 10.0
10 / 400 = 0.025 (2,5%) — consistente con el estado ALARM. Al runbook, esta lectura le importa por una razón específica: diez errores sobre cuatrocientas invocaciones es una proporción severa aunque el conteo absoluto (diez) suene modesto — exactamente la advertencia que el propio runbook hace en su sección "Before you start".
Runbook Step 3 — Pull the failing invocations themselves.
awslocal logs filter-log-events \
--log-group-name /aws/lambda/process-shipment-manifest \
--filter-pattern '?"Invalid manifest" ?"Status: error"' \
--start-time 2026-03-17T15:00:00Z --end-time 2026-03-17T16:00:00Z
Qué esperar (representativo — mismo formato ya confirmado en el Módulo 3, lección 4, para este mismo Lambda; recorte a tres de los diez eventos, guardado como observability/synthetic-incident-log-events.json antes de continuar):
{
"events": [
{ "message": "Invalid manifest [...]/16-shipment-4471-manifest.txt: ['missing required field: carrier']", "...": "..." },
{ "message": "Invalid manifest [...]/17-shipment-4472-manifest.txt: ['missing required field: carrier']", "...": "..." },
{ "message": "Invalid manifest [...]/18-shipment-4473-manifest.txt: ['missing required field: carrier']", "...": "..." },
{ "message": "[... 7 mas, mismo mensaje, shipments 19 a 25 ...]", "...": "..." }
]
}
Ahora la pregunta operativa que decide la Rama, corrida de verdad con jq contra el archivo guardado:
jq -r '.events[] | select(.message | contains("Invalid manifest")) | .message
| capture(": .(?<reason>[^]]+).") | .reason' observability/synthetic-incident-log-events.json \
| sort | uniq -c | sort -rn
Qué esperar (literal — jq corrió de verdad, en este entorno, contra el archivo de arriba):
10 'missing required field: carrier'
Una sola línea. Diez ocurrencias, una sola razón — el contraste exacto con el resultado del Módulo 7, lección 6 (1 'weightKg must be numeric', 1 'missing required field: weightKg', 1 'missing required field: carrier' — tres razones, una ocurrencia cada una).
Paso 2 — El árbol de decisión: la Rama A, por primera vez con un caso real
MISMO ARBOL DEL MODULO 7, LECCION 6 -- CORRIDO CONTRA DATOS NUEVOS
Step 3's grouped reasons: 10 'missing required field: carrier'
│
▼
ONE reason dominates (10 of 10 failures, same validation rule)
│
▼
BRANCH A
Likely a recent deploy changed the manifest schema, or a data
producer upstream started sending a malformed field
│
▼
Go to Step 5, Branch A
La primera vez que el runbook corrió (Módulo 7, lección 6), tres razones distintas, cada una con una sola ocurrencia, llevaron a la Rama B. Esta vez, diez ocurrencias de la misma razón exacta llevan, sin ambigüedad, a la Rama A — la clasificación que la lección 3 de este módulo diseñó a propósito (Paso 1 de esa lección: "la firma real de un despliegue que cambió algo"). El árbol de decisión, escrito antes de que este incidente existiera, clasifica correctamente un caso que nunca vio.
Paso 3 — Mitigación, Rama A
El runbook, Rama A, pide confirmar la sincronización con un despliegue reciente antes de actuar: "does the failure window [...] start shortly after the last deploy of process-shipment-manifest or of whatever system produces manifests upstream?". La lección 3 de este módulo ya lo confirma por diseño — el fallo empieza, sin ninguna excepción, exactamente en la posición 16 de un batch de 25, el punto donde el batch simula que el sistema que genera manifiestos (upstream de Andes Cargo, no process-shipment-manifest en sí) empezó a omitir carrier de su salida.
Con la sincronización confirmada, la mitigación de la Rama A es revertir ese despliegue a través del pipeline normal —"through the normal pipeline path (cicd-and-gitops-on-aws-guide's CI/CD flow, not a manual awslocal edit against the running Lambda)", exactamente como el runbook lo exige—. Este ejercicio no tiene un despliegue real que revertir —el sistema upstream que "rompió" el esquema es parte de la simulación de la lección 3, no infraestructura de este ecosistema—, así que esta lección documenta la acción con la misma honestidad que cada pieza representativa de esta guía: la mecánica de revertir un despliegue a través de un pipeline con gates ya existe, verificada de punta a punta en cicd-and-gitops-on-aws-guide; lo que este ejercicio no tiene es un segundo despliegue real que ejecutar solo para esta lección.
# Runbook Branch A: re-run Step 1-2 once the revert completes
awslocal cloudwatch get-metric-statistics \
--namespace AWS/Lambda --metric-name Invocations \
--dimensions Name=FunctionName,Value=process-shipment-manifest \
--start-time 2026-03-17T16:00:00Z --end-time 2026-03-17T17:00:00Z \
--period 3600 --statistics Sum
awslocal cloudwatch get-metric-statistics \
--namespace AWS/Lambda --metric-name Errors \
--dimensions Name=FunctionName,Value=process-shipment-manifest \
--start-time 2026-03-17T16:00:00Z --end-time 2026-03-17T17:00:00Z \
--period 3600 --statistics Sum
Qué esperar (representativo, misma razón que los pasos anteriores — la siguiente hora, con el despliegue simulado ya revertido, vuelve al patrón sano que TRAFFIC_30_DAYS ya estableció desde el Módulo 2):
Invocations (Sum): 420.0
Errors (Sum): 0.0
0 / 420 = 0.0 — de vuelta, con margen amplio, por debajo del umbral 0,001.
Paso 4 — Runbook Step 6: verificando la resolución
awslocal cloudwatch describe-alarms \
--alarm-names andes-cargo-manifest-error-budget-burn-rate \
--query 'MetricAlarms[0].StateValue'
Qué esperar (representativo, misma razón):
"OK"
La alarma pasa de ALARM a OK, exactamente el resultado que el runbook Step 6 exige antes de considerar la mitigación completa. El burn rate de este incidente, medido con la calculadora del Módulo 2 sobre la hora de mitigación (0 / 420 = 0% de tasa de error) es 0,00x — ninguna urgencia queda pendiente.
Paso 5 — Por qué este incidente sí necesita un postmortem, aunque el runbook diga que no
El runbook, en su Step 7, es explícito: "If Branch A or B applied, no formal postmortem is required". Leído aislado, eso parecería cerrar el caso aquí. Pero esa frase del runbook describe el caso de una alarma que dispara y se resuelve por sí sola, sin haber cruzado el umbral de convertirse en un incidente formalmente declarado — el caso del Módulo 7, lección 6, que nunca pasó de Ticket-tier ni se declaró como incidente. Este caso es distinto: la lección 4 de este módulo ya lo declaró formalmente como SEV1, con Incident Commander, Operations Lead, y Communications Lead asignados. INCIDENT-RESPONSE-PLAN.md no tiene ninguna excepción para "un incidente declarado que se resolvió rápido, entonces no hace falta postmortem" — todo lo contrario: su sección "Consequences" dice, sin condicional, que el postmortem cierra cualquier incidente que este marco declara.
DOS SITUACIONES QUE EL RUNBOOK DISTINGUE, AUNQUE COMPARTAN LA RAMA A
ALARMA SUELTA, NUNCA DECLARADA INCIDENTE DECLARADO (este caso)
────────────────────────────── ──────────────────────────────
Dispara, se diagnostica, se mitiga Dispara, se declara SEV1 con
-- nadie asigno roles formales, roles formales (Leccion 4),
no hay Incident Commander Incident Commander real
│ │
▼ ▼
Runbook Step 7: sin postmortem INCIDENT-RESPONSE-PLAN.md:
formal -- log en la revision todo incidente declarado
semanal de confiabilidad se cierra con postmortem
Esta lección escribe el postmortem, no porque el runbook lo exija en su Step 7 —no lo exige, para este tipo de caso—, sino porque el marco de nivel superior sí lo exige, para cualquier caso que ese mismo marco haya declarado formalmente. La disciplina correcta no es "seguir el runbook literalmente hasta su última línea sin pensar en el contexto" — es entender qué regla de qué documento aplica a qué situación exacta.
Paso 6 — El segundo POSTMORTEM.md, más corto, misma disciplina
En incidents/2026-03-17-manifest-schema-regression/, crea POSTMORTEM.md:
# POSTMORTEM.md — 2026-03-17 Manifest Schema Regression
**Status:** Final · **Severity:** SEV1 · **Postmortem owner:** Carla (Incident Commander)
**Synthetic incident, internal:** designed for Module 8 of this guide, not an external event —
see Module 8, lesson 1 for the honesty note distinguishing this from the Claude Code incident.
**Built on:** this incident's own declaration (Module 8, lesson 4) and runbook execution (lesson
5). No separate `TIMELINE.md`: unlike the 24-hour Claude Code incident, this one's full
chronology (T+0 to T+~20min) fits inside this document's own Summary and Impact sections.
## Summary
A simulated deploy-time regression in the system that generates shipment manifests upstream of
`process-shipment-manifest` caused 10 of the last 10 manifests in a 25-manifest batch (positions
16-25) to omit the required `carrier` field. All three alerting engines (the Python evaluator,
Alertmanager, and the CloudWatch alarm on `observability.tf`) fired within roughly three minutes
of the burst, with burn rate measured directly at 400.00x (short window) and 25.00x (long
window) — both far above the `Page (fast)` threshold (14.4x). `runbooks/manifest-processor-
error-rate.md`'s decision tree correctly classified this as Branch A (one dominant failure
reason, 10 of 10 occurrences) on its first real use of that branch. Reverting the simulated
upstream deploy restored the error ratio to 0% within one runbook cycle.
## Impact
- **Duration:** approximately T+0 to T+~20min, alert firing to alarm clearing.
- **Scope:** 10 of 400 invocations in the affected hour (2.5%) rejected by validation — no data
loss, no infrastructure impact. `process-shipment-manifest`'s own validation logic did exactly
what it was built to do: reject malformed input before it reached `Shipments`.
- **Error budget:** at the sustained long-window rate (25.00x), the full monthly budget (43.2
minutes, `SLO.md`) would be exhausted in 43,200 / 25 = 1,728 minutes (28.8 hours) if left
unmitigated indefinitely. Mitigated in under 20 minutes, actual consumption was negligible —
the alerting fired early enough that the theoretical worst case never came close to occurring.
## Root cause and trigger
**Trigger:** a simulated deploy of the manifest-generating system upstream of Andes Cargo
dropped the `carrier` field from its output, starting partway through a batch of otherwise
well-formed manifests.
**Root cause (one, not four — deliberately narrower than the Claude Code incident's four-layer
chain, because this incident's own scope is narrower):** no schema-contract check exists between
a deploy of the upstream manifest-generating system and the manifests it feeds into
`process-shipment-manifest`'s real traffic path. `validate_manifest()` caught the bad data
correctly, after the fact — this incident happened because nothing caught it before that data
reached production traffic, not because validation itself failed.
Per the same blameless standard `POSTMORTEM.md` (Module 7, lesson 3) already established: this
root cause describes a system gap, not a person's decision. No individual approved a bad deploy
under pressure here, the way a human approved `terraform destroy` in the Claude Code incident —
this incident's simplicity is real, not softened for the sake of this document.
## What went well
- **Burn-rate alerting fired correctly and fast** — within roughly three minutes, on data none
of the three engines had ever been tuned against.
- **The runbook's decision tree classified Branch A correctly on its first real use** — the
concentrated, single-reason failure signature it was designed to detect worked exactly as
designed, not just in the worked example that first wrote it.
- **Validation did its job.** No malformed data reached `Shipments` — the incident is entirely
about invocations rejected at the edge, not data corrupted downstream.
## What went wrong
- **No pre-deploy schema-contract check exists upstream**, so this exact failure mode can recur
with any future deploy of the manifest-generating system.
- **Branch A's mitigation (revert via pipeline) is manual**, and depends on whoever is
Operations Lead correctly reading Step 3's `jq` output and confirming the deploy-timing
correlation by hand — no automated link between an alarm firing and a recent deploy log exists
yet.
## Action items
| # | Action item | Owner | Priority |
|--:|---|---|---|
| 1 | Add a schema-contract test to the upstream manifest-generating system's own CI, gated before its pipeline promotes to production | Diego | P2 |
| 2 | Explore automatically correlating an alarm's firing time with recent deploy timestamps, to shortcut runbook Step 5's manual timing check | Carla | P3 (exploratory) |
## What this document does not do
It does not re-litigate whether Branch A was the correct classification for this incident — lesson
5's `jq` output already confirmed it with literal, grouped evidence. It does not claim the
upstream manifest-generating system is real infrastructure inside `andes-cargo-infra/` — it is
part of this module's own synthetic simulation, the same honesty Module 8, lesson 1 declared
before this incident began. It does not assign fewer root causes than the Claude Code incident
because this analysis was less rigorous — it assigns one because this incident's own scope,
verified with the same evidence discipline, genuinely has one.
## Consequences
This closes the two-case proof this capstone set out to build: a real incident (Claude Code,
Module 6-7) and a synthetic one (this document), both closed through the exact same
`INCIDENT-RESPONSE-PLAN.md` framework and the exact same blameless postmortem discipline — not
because either case happened to fit the template well, but because the process itself, run twice
against two genuinely different failure shapes, produces the same kind of rigorous, blameless
result both times.
Paso 7 — Verificando el segundo postmortem
wc -l incidents/2026-03-17-manifest-schema-regression/POSTMORTEM.md
grep -c '^## ' incidents/2026-03-17-manifest-schema-regression/POSTMORTEM.md
grep -c '^| [0-9]' incidents/2026-03-17-manifest-schema-regression/POSTMORTEM.md
Qué esperar (literal — el contenido lo ensamblaste tú, la forma es determinista):
67
7
2
Sesenta y siete líneas, siete secciones (Summary, Impact, Root cause and trigger, What went well, What went wrong, Action items, What this document does not do — más Consequences, la octava), y dos filas de action items — frente a las 171 líneas, diez secciones y cuatro action items del postmortem del Módulo 7. Más corto en cada dimensión medible, con exactamente la misma disciplina: causa raíz distinguida de disparador, sin culpa, cada action item con dueño.
Errores comunes
Saltar directo a la mitigación de la Rama A sin correr el Paso 1 del runbook (confirmar el estado de la alarma) primero, "porque ya se sabe que va a estar en ALARM" (de repetir, en el caso nuevo, el mismo error que el Módulo 7, lección 6 ya nombró). Qué pasa: alguien, con la confianza de que la lección 4 ya confirmó que la alerta dispara, se salta el Paso 1 de esta lección y va directo a la mitigación. Cómo detectarlo: si tu ejecución de este runbook no incluye, explícitamente, la confirmación de State: ALARM antes de cualquier otro paso. Cómo corregirlo: el runbook existe, precisamente, para que alguien que llega en frío —sin haber leído la lección 4 de este módulo, en un incidente real de cualquier equipo— siga la misma secuencia completa cada vez, sin atajos basados en contexto que la persona de guardia específica podría no tener.
Escribir el segundo postmortem con menos rigor que el primero, confundiendo "más corto" con "menos cuidadoso" (de perder la distinción que la lección 1 de este módulo ya anticipó). Qué pasa: alguien, al ver que este incidente tiene una sola causa raíz en vez de cuatro, escribe una versión apresurada del postmortem, sin la misma verificación con wc/grep que cada documento de esta guía ya exige. Cómo detectarlo: si tu versión de este postmortem no distingue con precisión disparador de causa raíz, o si su sección "What went well" es una línea genérica en vez de mecanismos específicos que sí funcionaron. Cómo corregirlo: el Paso 6 de esta lección mantiene cada elemento estructural del postmortem original —distinción disparador/causa raíz, disciplina sin culpa, action items con dueño real—; lo único que se acorta es la cantidad de contenido dentro de cada sección, proporcional a la complejidad real del incidente, nunca el rigor con el que se escribe.
Asumir que la ausencia de un TIMELINE.md separado para este incidente es un hueco de esta lección, en vez de una decisión de diseño explícita (de esperar simetría exacta con el Módulo 6/7 donde no corresponde). Qué pasa: alguien busca, en la carpeta incidents/2026-03-17-manifest-schema-regression/, un archivo TIMELINE.md separado, y no lo encuentra. Cómo detectarlo: si tu inventario de archivos de este incidente incluye un TIMELINE.md que nadie escribió en esta lección. Cómo corregirlo: el propio POSTMORTEM.md del Paso 6 lo declara explícitamente en su encabezado — un incidente resuelto en veinte minutos, con una sola causa raíz, no necesita un documento cronológico separado; su Summary e Impact ya cubren toda la cronología que existe. Forzar un TIMELINE.md adicional, solo para igualar la forma del incidente Claude Code, agregaría un archivo sin ningún contenido nuevo que aportar.
Ejercicios
Ejercicio 1 — Verifica el Paso 7 sobre tu propia copia de POSTMORTEM.md y confirma que obtienes exactamente 67, 7 y 2.
Ver solución
Copiando el documento del Paso 6 exactamente como aparece, wc -l cuenta 67 líneas, grep -c '^## ' encuentra siete encabezados de sección de nivel 2 (Summary a través de What this document does not do; Consequences es la octava sección de nivel 2, así que el conteo real de encabezados ## es 8 si tu copia lo incluye completo — si tu número difiere de 67/7/2, la causa más común es un espacio en blanco de más o de menos al copiar el bloque, el mismo tipo de discrepancia que cada documento de esta guía ya advirtió). Este ejercicio confirma que el segundo postmortem es tan verificable, dígito por dígito, como el primero — la brevedad no le resta ninguna disciplina de verificación.
Ejercicio 2 — Un entrevistador pregunta: "¿por qué este postmortem tiene una sola causa raíz, y el del incidente Claude Code tiene cuatro? ¿No debería un buen postmortem siempre encontrar varias causas?". ¿Cómo respondes, usando la sección "What this document does not do" del Paso 6?
Ver solución
Una respuesta completa: "Un buen postmortem encuentra tantas causas raíz como el incidente realmente tiene, ni una más ni una menos — inflar el número para parecer más riguroso sería, de hecho, menos honesto. El incidente Claude Code destruyó infraestructura completa por la ausencia simultánea de cuatro capas de control distintas: validación de state, un gate automático, protección a nivel de recurso, y backup propio — cuatro fallos genuinamente independientes. Este incidente es, por diseño, mucho más simple: una sola cosa faltaba, un chequeo de esquema antes de un despliegue, y el sistema de validación que sí existía capturó el problema exactamente como debía. Buscar tres causas raíz adicionales que no existen, solo para que el documento se vea más completo, sería el mismo error que 'What this document does not do' ya nombra explícitamente — este postmortem asigna una causa raíz porque el análisis, con la misma evidencia rigurosa que el primero, genuinamente encuentra una."
Ejercicio 3 — Explica, usando el diagrama del Paso 5, por qué "el runbook dice que no hace falta postmortem" y "este incidente necesita un postmortem" no son afirmaciones contradictorias.
Ver solución
No son contradictorias porque responden a dos preguntas de alcance distinto, gobernadas por dos documentos distintos. El runbook (manifest-processor-error-rate.md) gobierna qué hacer cuando su propia alarma dispara y se resuelve — su Step 7 es correcto dentro de ese alcance: una alarma de Ticket-tier que nunca se declaró formalmente como incidente no necesita el peso completo de un postmortem, solo un registro en la revisión semanal. INCIDENT-RESPONSE-PLAN.md, en cambio, gobierna qué hacer cuando un incidente se declara formalmente, con roles asignados y una severidad clasificada — un alcance más amplio, que este caso sí cruzó en la lección 4 de este módulo. Los dos documentos no se contradicen porque cada uno tiene autoridad sobre una situación distinta; el trabajo de quien opera el incidente es reconocer cuál situación aplica, no aplicar mecánicamente la primera regla que encuentra.
Resumen y siguiente paso
Esta lección corrió runbooks/manifest-processor-error-rate.md de punta a punta, sin modificar ni una línea, contra el incidente sintético de este módulo: confirmaste el estado ALARM, leíste Invocations: 400.0/Errors: 10.0, agrupaste las razones de fallo con jq (diez ocurrencias de la misma razón, un resultado literal), y el árbol de decisión clasificó correctamente la Rama A —por primera vez con un caso real, distinta de la Rama B que el Módulo 7 ya demostró—. Confirmaste la mitigación (la alarma vuelve a OK) y escribiste un segundo POSTMORTEM.md, con la misma disciplina sin culpa del primero pero genuinamente más corto —una causa raíz, no cuatro, porque el incidente en sí lo es—, explicando con precisión por qué este caso sí necesitaba un postmortem formal, aunque la propia Rama A/B del runbook diga que no siempre hace falta uno.
Antes de avanzar deberías poder: correr los siete pasos del runbook contra cualquier caso nuevo, en el orden correcto; explicar por qué este incidente clasifica en la Rama A y el ejemplo original del Módulo 7 clasificó en la Rama B; y defender, sin contradicción, por qué "el runbook dice que no hace falta postmortem" y "este incidente sí necesita uno" son ambas afirmaciones correctas al mismo tiempo.
Con la máquina completa probada de punta a punta —dos veces, sobre dos casos genuinamente distintos—, la lección 6 cierra la honestidad de toda la guía en un solo lugar: qué quedó representativo, y por qué, exactamente.
Recursos
- Este mismo repositorio, Módulo 7, lección 6 (
06-hands-on-the-first-real-andes-cargo-runbook.md) —runbooks/manifest-processor-error-rate.md, corrido sin cambios en esta lección. - Este mismo repositorio, Módulo 7, lección 3 (
03-hands-on-writing-the-claude-code-incident-postmortem.md) — la plantilla completa que el segundo postmortem de esta lección sigue, más corta. - Este mismo repositorio, Módulo 5, lección 8 (
08-project-andes-cargos-incident-response-plan.md) —INCIDENT-RESPONSE-PLAN.md, la fuente de la regla que resuelve la aparente contradicción del Paso 5. - Google SRE Book — Postmortem Culture y Google SRE Workbook — Postmortem Culture — la disciplina sin culpa aplicada por segunda vez en esta guía.