# `Mob.PostMortem`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/post_mortem.ex#L1)

Collect the post-mortems the OS and the BEAM leave behind when a
process dies, and put them onto `Mob.Defect.Bus` as capsules.

Three sources, each in its own submodule:

* `Mob.PostMortem.BeamCrashDump` — `erl_crash.dump` files (BEAM).
  Scans configured paths, reads a bounded 8 KB header, dedups by
  sha256, emits `:beam_crash` capsules.
* `Mob.PostMortem.IOS` — MetricKit payloads (crash / hang / launch /
  CPU / memory / disk). Attaches an `MXMetricManagerSubscriber`
  lazily on first sweep, buffers OS-delivered payloads in a bounded
  native queue, emits `:native_crash` / `:anr` / `:perf_regression`
  capsules on drain.
* `Mob.PostMortem.Android` — `ApplicationExitInfo` history (ANR,
  crash, OOM, user kill). Pulls the OS-held exit-reason list on
  demand (API 30+), filters against a persistent marker so the OS
  hands each exit over once, emits `:native_crash` / `:anr` /
  `:oom` / `:user_kill` capsules.

Both native drains are destructive, so `Mob.PostMortem.Journal`
keeps what they returned and every sweep emits it again, across
boots, until it has been observed on the bus.

Nothing here runs automatically. An app opts in by calling
`Mob.PostMortem.sweep/0` from its `on_start`, and a developer or CI
calls the same function from an IEx session (there is no dedicated
Mix task — the whole surface is one function). That matches the
discipline the `Mob.Defect` subsystem enforces: mob owns the format
and the bus but never becomes the collector.

## Idempotence

`sweep/0` is safe to call as many times as you like. Each artifact has
a sha256 id, and `Mob.PostMortem.Registry` records ids that have
already emitted; a subsequent sweep finds them seen and does nothing.
The dump stays on disk — nothing here deletes or moves a file — so
offline inspection still works. See the registry's moduledoc for why
the seen-set does not persist across BEAM restarts (a fresh BEAM
sweeps back into a fresh bus, on purpose), and the journal's for
why native entries are re-emitted until observed.

## Return value

`sweep/0` and `sweep/1` return the capsules that were emitted this
call, in the order they were emitted. That is what a caller drop-in
in `on_start` normally ignores; a test or a CLI wants it.

# `sweep`

```elixir
@spec sweep() :: [Mob.Defect.Capsule.t()]
```

Sweep every source using its default paths.

Equivalent to `sweep([])` — each source module supplies its own
defaults (see `BeamCrashDump.default_paths/0`; iOS and Android draw
from their platform native APIs when linked).

# `sweep`

```elixir
@spec sweep([Path.t()]) :: [Mob.Defect.Capsule.t()]
```

Sweep every source, with `beam_paths` added to the BEAM-crash-dump
scanner's default paths.

A caller with dumps somewhere unusual (a CI artifact directory, a
scratchpad) passes them here — they merge with the defaults rather
than replacing them, so passing an empty list is equivalent to
`sweep/0`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
