Construir uma visão útil do impacto
Num incidente fictício de fundos, o serviço A reporta oitenta contas afetadas e B cinquenta. Vinte aparecem em ambos. Com o mesmo identificador e período, a união tem 110 contas, não 130. Se B contar sessões em vez de contas, falta uma correspondência antes de calcular pessoas distintas. Regista unidade, intervalo observado, origem e limitações junto do número. Acrescenta a capacidade afetada: consultar uma posição, entregar um ficheiro ou validar um resultado são compromissos diferentes. Às 15:42 o batch está parado e a entrega validada vence às 16:30. Como a validação exige doze minutos depois do processamento, este precisa de terminar até às 16:18. Há 36 minutos desde a observação, mas essa margem não prova que uma opção de recuperação cabe. Pede uma estimativa das etapas necessárias e o momento em que a alternativa deve ser decidida. Os valores são condições deste exercício, sem representar prazos reais de um banco.
Dividir análise mantendo coordenação
Quando doze especialistas discutem três hipóteses em simultâneo, a chamada pode deixar de produzir decisões claras. Uma frente precisa de uma questão, líder, participantes, limites de intervenção e ponto de retorno. Mantém uma visão comum do impacto e das ações que podem afetar outras frentes. No exemplo, rede quer reiniciar X enquanto aplicação recolhe estado volátil de X. A recolha é uma leitura, mas o reinício pode destruir a observação. Ambas as frentes também reservaram Sofia, que só executa uma tarefa exclusiva de cada vez. Há duas dependências diferentes: o estado de X e a capacidade de Sofia. Outro especialista pode resolver a segunda sem resolver a primeira. Pede aos líderes uma sequência ou alternativa que preserve o resultado necessário. A orientação pública PagerDuty sobre incidentes complexos ajuda a pensar em frentes e limites de coordenação; os nomes de equipa e regras internas desse fornecedor não são requisitos do teu empregador.
Confirmar tarefas e capacidade de liderança
“Rui compara configurações” é uma atribuição, não prova de aceitação, início ou conclusão. No registo, distingue esses estados e pede um retorno com resultado ou bloqueio. Se às 11:13 venceu o retorno das 11:12, confirma estado e decide apoio, extensão ou alternativa. Não transformes a extensão de investigação numa promessa de recuperação. Se alguém identifica risco de perda de dados, clarifica a condição antes de executar uma opção popular na chamada. Uma objeção pode ser relevante sem probabilidade exata. Também não forces “sim” ou “não” quando um consumidor é desconhecido: regista a incerteza e a verificação que a pode reduzir. Quando a coordenadora repete pedidos e perde retornos, organiza cobertura e passagem. A chegada de um diretor não transfere automaticamente a liderança. O estado partilhado deve continuar a indicar quem coordena, quem decide dentro do mandato local e quem executa cada intervenção.
Executar o modelo e explicar os seus limites
Guarda o código completo abaixo como incident-command.py e executa python3 incident-command.py. As catorze verificações usam dados fictícios para calcular união de impacto e margem temporal, detetar recursos declarados em comum, distinguir estados de tarefas e analisar lacunas de passagem. Uma confirmação de v7 não cobre uma ação iniciada em v8. Mesmo quando os campos coincidem, o programa não verifica compreensão humana nem disponibilidade de canais. Também não descobre dependências omitidas: uma lista sem interseção significa apenas ausência de conflito nas entradas fornecidas. Muda o recurso de uma ação e prevê a diferença no resultado antes de voltar a executar. Depois escreve uma nota de coordenação com impacto, ação incompatível, capacidade necessária e próximo retorno. No trabalho real, essa nota deve ser discutida com os responsáveis, dentro do processo aplicável. O exercício não envia mensagens, não modifica infraestrutura e não autoriza ações de produção.
"""Original DR incident coordination model; supplied fictional observations only."""
import hashlib
import json
import platform
from pathlib import Path
from statistics import median
def union_impact(left, right, left_unit, right_unit):
if left_unit != right_unit:
raise ValueError("Map units and identities before combining populations")
return len(set(left) | set(right))
def interference(actions):
conflicts = []
for i, first in enumerate(actions):
for second in actions[i + 1:]:
resources = sorted(set(first["resources"]) & set(second["resources"]))
people = sorted(set(first["exclusivePeople"]) & set(second["exclusivePeople"]))
if resources or people:
conflicts.append({"actions": [first["id"], second["id"]],
"resources": resources, "exclusivePeople": people})
return conflicts # Possible interference for review, not an execution scheduler.
def task_state(task):
if not task.get("accepted"):
return "assigned-unconfirmed"
if not task.get("started"):
return "accepted-not-started"
if not task.get("completed"):
return "in-progress"
return "completed-result-recorded" if task.get("result") else "completion-claimed-result-missing"
def handover_gaps(current_version, acknowledgment, required_actions):
gaps = []
if acknowledgment.get("version") != current_version:
gaps.append("state-version")
if not acknowledgment.get("incomingOwner"):
gaps.append("incoming-owner")
missing = sorted(set(required_actions) - set(acknowledgment.get("actions", [])))
if missing:
gaps.append("uncovered-actions:" + ",".join(missing))
return gaps # Empty means only that these supplied fields match.
def reconcile(expected, received):
expected, received = set(expected), set(received)
return {"missing": sorted(expected - received),
"unexpected": sorted(received - expected)}
def run():
checks = []
def check(name, actual, expected):
if actual != expected:
raise AssertionError((name, actual, expected))
checks.append({"name": name, "actual": actual, "passed": True})
a, b = list(range(1, 81)), list(range(61, 111))
check("overlapping_accounts_are_counted_once", union_impact(a, b, "account", "account"), 110)
try:
union_impact(a, b, "account", "session")
mismatch_rejected = False
except ValueError:
mismatch_rejected = True
check("different_units_require_mapping", mismatch_rejected, True)
observed, due, validation = 15 * 60 + 42, 16 * 60 + 30, 12
processing_due = due - validation
check("sequential_validation_reduces_margin", {"processingDue": f"{processing_due // 60:02}:{processing_due % 60:02}",
"minutesFromObservation": processing_due - observed}, {"processingDue": "16:18", "minutesFromObservation": 36})
actions = [{"id": "restart-X", "resources": ["X"], "exclusivePeople": ["Sofia"]},
{"id": "capture-X", "resources": ["X"], "exclusivePeople": ["Sofia"]}]
check("workstreams_share_target_and_person", interference(actions),
[{"actions": ["restart-X", "capture-X"], "resources": ["X"], "exclusivePeople": ["Sofia"]}])
separate = [actions[0], {"id": "prepare-Y", "resources": ["Y"], "exclusivePeople": ["Luis"]}]
check("no_declared_overlap_is_not_proven_independence", interference(separate), [])
check("task_states_do_not_collapse", [task_state(t) for t in [
{"owner": "Rui"}, {"accepted": True}, {"accepted": True, "started": True},
{"accepted": True, "started": True, "completed": True},
{"accepted": True, "started": True, "completed": True, "result": "comparison recorded"}]],
["assigned-unconfirmed", "accepted-not-started", "in-progress", "completion-claimed-result-missing", "completed-result-recorded"])
ack = {"version": 7, "incomingOwner": "Marta", "actions": ["compare"]}
check("old_acknowledgment_does_not_cover_changed_state", handover_gaps(8, ack, ["compare", "failover"]),
["state-version", "uncovered-actions:failover"])
current_ack = {"version": 8, "incomingOwner": "Marta", "actions": ["compare", "failover"]}
check("matching_fields_do_not_prove_human_understanding", handover_gaps(8, current_ack, ["compare", "failover"]), [])
action = {"approved": True, "executionConfirmed": False, "effectValidated": False}
check("approval_is_not_execution_or_effect", [action[k] for k in ["approved", "executionConfirmed", "effectValidated"]], [True, False, False])
check("equal_counts_can_hide_different_identities", reconcile(["A", "B", "C", "D"], ["A", "B", "C", "X"]), {"missing": ["D"], "unexpected": ["X"]})
check("partial_validation_keeps_residual_visible", len(reconcile(range(240), range(232))["missing"]), 8)
observation, change, received = 14 * 60 + 5, 14 * 60 + 12, 14 * 60 + 20
check("receipt_time_does_not_refresh_observation", {"observationPredatesChange": observation < change,
"receivedAfterChange": received > change, "observationAgeMinutes": received - observation},
{"observationPredatesChange": True, "receivedAfterChange": True, "observationAgeMinutes": 15})
before = [20, 25, 40, 70, 90]
after_exclusion = [20, 25, 40]
check("selection_changes_median_without_new_outcomes", [median(before), median(after_exclusion)], [40, 25])
criterion = {"backlogProcessed": True, "reconciliationAccepted": False}
check("recovery_claim_still_has_validation_gap", [k for k, v in criterion.items() if not v], ["reconciliationAccepted"])
return {"scope": "Fictional set, arithmetic and record checks only. No real incident commanded, production authorization, channel availability, human handover comprehension or group exercise is established. Shared-resource checks detect only declared overlaps and do not prove safe concurrency.",
"python": platform.python_version(), "scriptSha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
"groups": len(checks), "checks": checks}
if __name__ == "__main__":
print(json.dumps(run(), ensure_ascii=False, indent=2))Duas frentes partilham X e Sofia. A coordenação identifica ambas as restrições antes de prometer prazos; 110 contas distintas e limite de processamento às 16:18 dão contexto à decisão.
Armadilhas comuns
Somar unidades diferentes; duplicar capacidade; interpretar atribuição como execução; resolver risco por popularidade; transferir liderança sem estado atual.
Tópicos relacionados: Impacto e prioridades · Passagem de turno · Dependências e capacidade
Divide o trabalho de análise, mas mantém explícitas as dependências, a capacidade e a responsabilidade pela próxima decisão.
Referência: Complex Incidents · Google SRE incident guidance; PagerDuty contextual incident model; NIST SP 800-61 Rev. 3 April 2025; editorial review 2026-10-01