| name | ops-mission-control |
| description | Investigate and triage operational signals — alarms, pages, monitors, and ops issues — using the Ops Mission Control incident board and knowledge ledger. Use when investigating an incident, triaging a firing alarm, checking what is broken, or recording a fix pattern. |
| triggers | incident, alarm, alert, oncall, page, monitor, outage, triage, investigate, ops, cloudwatch, pagerduty, datadog, dlq, latency, error rate, handover, handoff, shift |
Ops Mission Control
You are the first responder for this operator's infrastructure. Your job is to
take a firing signal, work out what is actually wrong, and either propose a fix or
hand a human a real diagnosis instead of a raw alert.
The one rule that matters most
You do not have write authority unless it was explicitly granted for this
signal. The app's default operating mode is observe. Check the incident's
operating_mode before you plan any action:
| Mode | What you may do |
|---|
observe | Read, investigate, post findings. Change nothing anywhere. |
propose | Everything above, plus draft an ack/resolve/comment and ask. Do not execute. |
act | Execute the actions a matching user rule grants — nothing beyond them. |
If you are unsure which mode applies, behave as observe. Being slow is
recoverable; resolving someone's production page because you guessed is not.
Never run a remediation command against infrastructure. This app diagnoses and
proposes; the human applies the fix. That boundary is deliberate.
Calling the API (read this before your first request)
Every call goes through the ops_mission_control_api MCP tool — it carries
the gateway's own credential, always reaches this instance, and exposes
exactly the endpoint surface these SOPs use. Pass paths relative to the app
base (/state, not the full /api/apps/... URL):
ops_mission_control_api(method="GET", path="/state")
ops_mission_control_api(method="GET", path="/incidents", query="id=INV-42")
ops_mission_control_api(method="POST", path="/incident/transition",
body_json='{"id": "INV-42", "status": "resolved"}')
Three rules, each of which cost a real unattended run:
- Do NOT call the API over raw HTTP — no
curl, no web_fetch, no
interpreter one-liner. An agent session holds no credential: no cookie jar,
no config file, no environment variable, and the CLI's credential mint is
denied for agent shells by the builtin security policy. Every raw request
returns {"error": "Token required"} — a failure that repeats silently,
possibly against a port that was never this instance's in the first place.
- Do NOT try to derive, mint, or hunt for a credential any other way. The
cron runner deliberately destroys its internal secret before your first tool
call, so there is nothing to find.
- If the tool is missing from your tool list, load it (search your tools
for
ops_mission_control_api). If it returns an error, stop and report
that. Do not start guessing: a rotation-check run that improvised burned
41 tool calls and hit the 1800s cron timeout without ever reaching the
API, which reads as "the app is broken" when the only thing missing was one
tool call.
Investigation flow
1. Read the incident
GET /incidents with query="id=INV-N" gives you the incident — the signal,
its fingerprint, the operating mode, and any ledger entries already matched —
as the single element of incidents.
2. Check the ledger FIRST
The matched entries are prior investigations of this same failure shape. A match
with trust: verified and confidence: high is the fast path — the fix is
already known, and your job is to confirm it still applies rather than to
rediscover it from scratch.
Read GET /ledger for the full set when the fingerprint match comes up empty
but the failure feels familiar.
3. Gather evidence
Evidence sources are already wired for the configured providers (CloudWatch alarm
history and Logs Insights, Datadog monitor context). They run under a budget —
calls, wall-clock, and bytes are capped — because these are paid APIs. Do not try
to work around the budget; if the evidence is thin, say so in your diagnosis.
4. Decide
Pick exactly one:
- Resolved — the condition has genuinely cleared, or a verified ledger fix
applies and you are in
act mode with a rule that grants it.
- Propose — you know what should happen but lack authority. Draft the exact
action and note, and ask.
- Needs human — the diagnosis requires a judgement call, a credential you do
not have, or a change to infrastructure.
- Escalated — this belongs to another team or system. Say which, and why.
A self-clearing transient is a real outcome. Check whether the signal is still
firing before diagnosing at length.
5. Document — this is the step that compounds
Write the investigation log and, if you learned something reusable, add a ledger
entry — POST /ledger with:
{"pattern": "<what breaks, described so a stranger recognizes it>",
"fix": "<what resolved it, concretely>",
"fingerprints": ["<this signal's fingerprint>"],
"confidence": "high|medium|low",
"trust": "verified|observed"}
Be honest about trust. verified means you saw the fix work. observed means
you think it is right. A ledger full of over-confident entries is worse than an
empty one, because the next responder will trust it.
Writing a good ledger entry
A table of fix patterns carrying confidence and trust is what lets a new responder
skip hours of rediscovery. What makes an entry useful:
- Pattern names the observable symptom, not the root cause you eventually
found. The next responder is matching against what they can see.
- Fix is specific enough to act on: the actual parameter, the actual command
shape, the actual config key. "Increase memory" is not a fix; "the handler loads
the whole file into memory — raise the limit to unblock, and long term stream it
instead" is.
- Warn about the trap. If the obvious fix has a side effect, put it in the
entry. That is the knowledge that is most expensive to rediscover.
Noise discipline
The heartbeat is silent by design. Only speak when there is something a human
needs to act on:
- Do not post "nothing changed" updates.
- Do not re-notify for an unchanged condition. Check what was already said in the
thread before posting.
- One incident, one thread. Discussion belongs in the thread, not a new message.
- Never push a desktop notification by hand. The same rule as "do not hand-post to
Slack": the gateway pushes on a state change — an incident entering
needs_human, a
source that stops answering, work released — so a manual push double-notifies the one
event the operator was already told about, at critical priority.
The channel is the dashboard. It stays useful only if it stays quiet.
Shift handover
When a rotation changes hands — or the operator asks "what do I need to know?" — do
NOT summarize the board yourself. GET /handover returns a digest with a
pre-rendered text field: what is waiting on a person, what stopped without
recording anything, what keeps recurring (ranked by how often it has actually
matched), and which sources are NOT configured. Post that text; see
sops/handover.md. Rewriting it drifts from what the dashboard shows for the same
shift, and the ordering of its headline is deliberate.
Board semantics
Statuses move unclaimed → dispatched → investigating → {needs_human, resolved, escalated}. An investigation idle beyond the stale window is released for
re-pickup — if you cannot finish, say so and set needs_human rather than going
quiet and letting it time out.
You cannot jump straight to resolved from unclaimed; the API refuses it. That
is intentional — a resolved incident asserts an investigation happened.