heartbeat: notify Michael by email when each queued task completes

Adds notify_task_complete/3 called after every queue task resolves
(done, blocked, or failed). Posts to Telepathy's POST /email endpoint.
Also adds :inets and :ssl to extra_applications so :httpc is available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Opus 4.6 2026-05-18 21:21:49 +00:00
parent 6271e973a6
commit 11a9026597
2 changed files with 56 additions and 1 deletions

View File

@ -120,12 +120,15 @@ defmodule Symbiont.Heartbeat do
"Heartbeat: task #{task["id"]} returned but self-reported blocked — marking blocked" "Heartbeat: task #{task["id"]} returned but self-reported blocked — marking blocked"
) )
Symbiont.Queue.block(task["id"], text) Symbiont.Queue.block(task["id"], text)
notify_task_complete(task, :blocked, text)
else else
Symbiont.Queue.complete(task["id"], text) Symbiont.Queue.complete(task["id"], text)
notify_task_complete(task, :done, text)
end end
{:error, reason} -> {:error, reason} ->
Symbiont.Queue.fail(task["id"], inspect(reason)) Symbiont.Queue.fail(task["id"], inspect(reason))
notify_task_complete(task, :failed, inspect(reason))
end end
end) end)
end) end)
@ -186,4 +189,56 @@ defmodule Symbiont.Heartbeat do
defp schedule_next(interval) do defp schedule_next(interval) do
Process.send_after(self(), :tick, interval) Process.send_after(self(), :tick, interval)
end end
defp notify_task_complete(task, status, result) do
task_id = task["id"] || "unknown"
task_desc = task["task"] || ""
priority = task["priority"] || "normal"
subject =
case status do
:done -> "Task done [#{priority}]: #{String.slice(task_desc, 0, 60)}"
:blocked -> "Task blocked [#{priority}]: #{String.slice(task_desc, 0, 60)}"
:failed -> "Task failed [#{priority}]: #{String.slice(task_desc, 0, 60)}"
end
snippet = String.slice(result || "", 0, 800)
body = """
Task #{task_id} completed with status: #{status}
Description:
#{task_desc}
Result:
#{snippet}
Muse
"""
payload =
Jason.encode!(%{
to: "mdwyer@michaelmdwyer.com",
subject: subject,
body: body,
from_name: "Muse"
})
url = ~c"http://localhost:8114/email"
try do
:httpc.request(
:post,
{url, [], ~c"application/json", payload},
[{:timeout, 10_000}],
[]
)
Logger.info("Task notification sent: #{task_id}#{status}")
rescue
e -> Logger.warning("Task notification failed for #{task_id}: #{Exception.message(e)}")
catch
_, reason -> Logger.warning("Task notification failed for #{task_id}: #{inspect(reason)}")
end
end
end end

View File

@ -15,7 +15,7 @@ defmodule Symbiont.MixProject do
def application do def application do
[ [
extra_applications: [:logger, :crypto], extra_applications: [:logger, :crypto, :inets, :ssl],
mod: {Symbiont.Application, []} mod: {Symbiont.Application, []}
] ]
end end