hermes
research
decision-grade
Pattern A+B mixed — single trial
Fleet-wide adoption arc · 2 sub-agents + main-thread synthesis · 4 deliverables
T1 — Pantheon Scheduling Stack — Fleet-Wide Capability Profile
Companion: T2-nous-hermes-fleet-profile.md (mirror axes for matrix join in T3).
Source arcs: scheduled-tasks-fleet-protocol v0 (2026-05-20) + n8n-mastra-slack (2026-05-24).
Lens: fleet-wide adoption candidate evaluation. Not viska-specific.
Stack inventory (executors covered)
- launchd — per-machine, K20N0S primary, plist in
~/Library/LaunchAgents/. Themis runs on it. src: sources/2026-05-20.../07-launchd-vs-cron-macos.md
- Claude Code
/loop — session-scoped, CronCreate/CronList/CronDelete, min 1min, 7-day expiry, no catch-up, ≤50 tasks/session. src: sources/2026-05-20.../01-claude-code-scheduled-tasks-loop.md
- Claude Code Routines (cloud) — Anthropic-managed, durable, min 1h, schedule+API+GitHub triggers, per-operator-account identity, daily run cap, "green ≠ success". src: sources/2026-05-20.../02-claude-code-routines-cloud.md
- Claude Code Desktop scheduled tasks —
~/.claude/scheduled-tasks/<name>/SKILL.md, cadence in Desktop-internal store, 1 catch-up on wake. src: sources/2026-05-20.../03-claude-code-desktop-scheduled-tasks.md
- n8n schedule-trigger — proteus-hosted hostinger-vps, single main
EXECUTIONS_MODE=regular. Native trigger+action nodes for ~14 services NATIVE-T+A. src: 2026-05-24/sources/T2-n8n-architecture.md
- Mastra Workflow — mastra-bridge, atlas-owned, K20N0S
localhost:3002. Wraps Agent LLM step in deterministic validate→analyze→write→post chain. src: 2026-05-24/sources/T4-mastra-agent.md
- Mastra Agent — LLM step inside Workflow;
@mastra/core@1.35.0 (May 2026), 19.8k stars, 300k weekly DLs. Tool-constrained "analyze-only" in fleet posture.
- Slack —
chat.postMessage primary delivery (returns ts), workspace-isolated rate budgets, slack-boas (metis) + slack-viska (mimir). Bot tokens via Hades. src: 2026-05-24/sources/T5-slack-interfaces.md
Axis profiles
Scoring: 0 = not supported, 1 = barely, 2 = workable with friction, 3 = solid, 4 = strong, 5 = best-in-class.
Axis 1 — Triggering modes
| Executor |
cron |
event |
manual |
conversational |
file-watch |
score |
note |
| launchd |
✓ |
✗ |
✓ (launchctl start) |
✗ |
✗ (kqueue possible, unused) |
2 |
StartCalendarInterval only. src: 07-launchd §plist |
/loop |
✓ |
✗ |
✓ in-session |
✓ (Claude can author cron mid-turn) |
✗ |
2 |
Session-bound; conversational is unique |
| Routines |
✓ |
✓ (GitHub webhook, API trigger) |
✓ (per-routine API POST) |
✗ |
✗ |
4 |
Three triggers attachable + combinable. src: 02 |
| CC Desktop |
✓ |
✗ |
✓ via UI |
✓ (self-modify via update_scheduled_task — FORBIDDEN in fleet) |
✗ |
2 |
Local only. src: 03 |
| n8n |
✓ (multi-rule per node) |
✓ (webhooks, native triggers) |
✓ (Execute Workflow) |
✗ |
✓ (Local File Trigger) |
4 |
Broadest surface. src: T2 §1 |
| Mastra Workflow |
✗ native (in-process setInterval fragile) |
✓ (HTTP) |
✓ (POST endpoint) |
✗ |
✗ |
2 |
Not a scheduler; downstream consumer. src: T4 §schedule-caveat |
| Slack |
✗ |
✓ (Events API, slash, interact) |
✓ (slash command) |
✓ (mention thread) |
✗ |
3 |
Trigger surface only, not executor |
Axis 2 — Persistence + state durability
| Executor |
restart |
reboot |
sleep |
state location |
score |
| launchd |
✓ |
✓ |
✓ catches up on wake |
~/Library/LaunchAgents/ plist |
5 |
/loop |
✗ |
✗ |
✗ (must be open session) |
session memory; restored on --resume if unexpired; 7d max |
1 |
| Routines |
✓ |
✓ |
n/a (cloud) |
claude.ai cloud per-account |
5 |
| CC Desktop |
✓ |
✓ |
✗ sleep skips (catch-up: 1 most-recent missed) |
Desktop-internal store + SKILL.md on disk |
3 |
| n8n |
✓ |
✓ (process supervised) |
n/a (server) |
n8n Postgres executions table; 30d max (EXECUTIONS_DATA_MAX_AGE) |
3 — silent-stall failure mode (CRIT, 2yr unresolved). src: T2 footgun #2 |
| Mastra Workflow |
✓ |
✓ |
n/a |
@mastra/pg against Supabase Postgres |
4 |
| Slack |
✓ (Slack-managed) |
n/a |
n/a |
Slack workspace storage |
5 (as delivery surface) |
| Executor |
tool surface available at runtime |
score |
| launchd |
shell ProgramArguments only; any executable on disk |
2 |
/loop |
full session tools (MCP, plugins, subagents inherit) |
5 |
| Routines |
connectors configured per task; MCP via "Connectors" config; no permission prompts during run; allowlist network egress |
4 |
| CC Desktop |
config files + connectors; MCP servers per-task; permission mode per-task |
4 |
| n8n |
400+ native nodes (14 NATIVE-T+A in fleet scope), Code node, HTTP Request, Execute Sub-workflow. src: T1-services-landscape |
5 |
| Mastra Workflow |
Mastra Tools (Zod-typed), sub-Agents, sub-Workflows, MCP servers, 11 OTel exporters. src: T4 §1,2 |
4 |
| Slack |
Block Kit + chat.postMessage variants + thread_ts |
n/a (delivery, not invocation) |
Axis 4 — Cost model
| Executor |
cost |
score (cost-efficiency) |
| launchd |
$0 — OS-native |
5 |
/loop |
rolled into Claude Code subscription |
4 |
| Routines |
subscription-tier daily run cap; one-offs exempt. src: 02 §limits |
3 |
| CC Desktop |
rolled into Claude Code subscription |
4 |
| n8n |
self-host on hostinger-vps (fixed VPS cost); Enterprise gates multi-main + External Secrets. src: T2 footgun #1, #7 |
4 |
| Mastra Workflow |
self-host; LLM API pay-per-token for Agent step |
3 |
| Slack |
free workspace tier covers fleet volume; per-app-per-workspace rate budget |
5 |
Axis 5 — Isolation / sandbox posture
| Executor |
process |
network |
secret |
tenant-boundary |
score |
| launchd |
OS process |
none enforced |
env vars + Keychain |
per-user (LaunchAgents vs LaunchDaemons) |
3 |
/loop |
inherits session |
session permissions |
session env |
session-scoped |
2 |
| Routines |
cloud session |
Trusted-default allowlist (403 host_not_allowed) |
claude.ai-stored OAuth |
per-operator-account (NOT per-fleet-agent — identity leakage). src: 02 §identity |
3 |
| CC Desktop |
local process; worktree-isolation toggle (off-by-default — fleet MUST force on) |
inherits machine |
inherits machine |
per-machine |
3 |
| n8n |
single-main process all workflows share; queue mode adds worker pool |
per-workflow HTTP egress (no allowlist) |
n8n DB encrypted-but-not-rotated; Enterprise External Secrets only |
per-workflow; no namespace isolation between mimir-n8n + pantheon-ops authorship |
2 — multi-replica double-fire is a tenant-boundary breach. src: T2 footgun #1 |
| Mastra Workflow |
one Node process per mastra-bridge instance |
mastra-bridge ACL via Tailscale token |
per-workflow env / Hades-deposited |
per-workflow + per-Agent (Tool constraint = sandbox) |
4 — analyze-only Agent posture eliminates LLM-driven side-effect class |
| Slack |
n/a |
n/a |
bot-token per workspace (Hades) |
workspace-level (slack-boas vs slack-viska budgets independent). src: T5 §3 |
4 |
Axis 6 — Observability
| Executor |
logs |
traces |
rule-18 evidence integration |
score |
| launchd |
StandardOutPath/StandardErrorPath keys per-plist (BOTH required or silent loss) |
none |
application-emitted only |
2 |
/loop |
session transcript |
transcript only |
none |
1 |
| Routines |
claude.ai run list — "green ≠ success" built-in anti-pattern. src: 02 §observability-gap |
none |
hostile by default — rule-18 violation surface |
1 |
| CC Desktop |
Desktop session list |
none |
none |
2 |
| n8n |
executions table, /api/v1/executions REST, /metrics self-host only (NOT cloud). src: T2 footgun #13 |
n/a |
application-emitted via collect-evidence terminal step |
3 |
| Mastra Workflow |
per-step LLM/Tool/Workflow spans via 11 first-party OTel exporters (Langfuse, Datadog, etc.). src: T4 §5.3 |
✓ first-class |
deterministic writeRun step persists verification_evidence JSONB; rule-18 CHECK constraint enforces at DB layer. src: REPORT §7.3 |
5 |
| Slack |
per-message ts = delivery proof; permalink audit |
n/a |
slack_message_ts in verification_evidence JSONB. src: REPORT §7.1 |
4 |
Axis 7 — Multi-tenant boundary
| Executor |
boundary |
score |
| launchd |
per-machine, per-user. K20N0S is shared executor — agents distinguished only by plist Label namespace. |
2 |
/loop |
session = tenant; no cross-tenant possible |
4 |
| Routines |
per-operator-account. ONE account holds all fleet routines — collapses agent identity into operator. src: 02 §identity |
1 |
| CC Desktop |
per-machine; per-task permission mode |
3 |
| n8n |
per-workflow; mimir-n8n + pantheon-ops author same instance — no namespace; double-fire on multi-replica DOSes shared Postgres + downstream APIs. src: T2 footgun #1 |
2 |
| Mastra Workflow |
per-workflow; per-Agent Tool-constrained; can deploy per-tenant mastra-bridge instances |
4 |
| Slack |
per-workspace per-app rate budget; slack-boas noise cannot DOS slack-viska. src: T5 §3 / REPORT §6.3 |
5 |
Axis 8 — Failure modes + auto-pause
| Executor |
retry |
auto-pause |
silent-stall risk |
score |
| launchd |
KeepAlive semantics; no retry-on-fail backoff knob |
✗ (manual) |
silent skip if StandardErrorPath missing |
2 |
/loop |
no retry semantics |
✗ |
session-close = loss |
1 |
| Routines |
platform retry opaque |
✗ |
per-account daily cap may silently drop |
1 |
| CC Desktop |
per-task; no backoff knob |
✗ |
sleep-through = skip (1 catch-up max) |
2 |
| n8n |
Retry-On-Fail (no backoff knob); Error Workflow pattern. src: T2 §3, footgun #12 |
✗ native (Inngest-style auto-pause-after-N not built-in) |
CRIT silent-stall (2yr unresolved). src: T2 footgun #2 |
2 |
| Mastra Workflow |
per-step retry + workflow-level error branch |
✗ native (built fleet-side via PRD §3 ladder) |
low — observable via OTel spans |
4 |
| Slack |
platform handles |
n/a |
rate-limit storm visible via 429 / ratelimited |
4 |
Fleet-applied compensations (designed-in, not executor-native): PRD v0 §3 ladder = 1 failure log → 2 handoff → 3 paused_by_failure=true + athena decision-request → Cerberus P-SCHED-5..8 probes. src: PRD §3, REPORT §8.2
Axis 9 — Learning / feedback loop
| Executor |
reflection step |
learns from outcome rows |
score |
| launchd |
✗ |
✗ |
0 |
/loop |
conversational |
session memory only |
2 |
| Routines |
✗ |
✗ |
0 |
| CC Desktop |
session generates SKILL.md body edits; update_scheduled_task MCP allows self-rescheduling (FORBIDDEN in fleet). src: 03 §self-modifying |
1 (fleet-disabled) |
1 |
| n8n |
✗ |
✗ |
0 |
| Mastra Workflow |
analyze step LLM produces reflection_text stored alongside evidence in fleet_schedule_runs; downstream consumers can mine the corpus. Memory primitive (@mastra/memory) available but not wired in fleet posture. src: T4 §1, REPORT §1.1 |
3 |
|
| Slack |
thread analysis (Shape A) lands on same ts parent for compounding context. src: REPORT §6.2 |
2 |
|
Stack-level gap: no feedback loop closes today. reflection_text is written but no executor reads it back to adjust cadence, retry policy, or evidence-shape expectations. Adoption candidate axis.
Axis 10 — Cross-machine + sleep/wake survival
| Executor |
wake catchup |
cross-machine |
needs always-on |
score |
| launchd |
✓ wake-catchup primary advantage over cron. src: 07 |
✗ per-host (executor_machine field disambiguates). src: PRD §5 |
K20N0S no-sleep configured |
4 |
/loop |
✗ |
✗ |
session must be open |
0 |
| Routines |
n/a (cloud) |
✓ — API trigger reachable from any Tailscale host. src: 02 §triggers |
✗ |
5 |
| CC Desktop |
1 catch-up of most-recent missed; "Keep computer awake" required; closing lid still sleeps. src: 03 §missed-run |
✗ per-machine |
machine must run |
2 |
| n8n |
✗ no missed-run replay; silent skip. src: T2 §1 failure-modes |
✓ (hostinger-vps) |
hostinger-vps always-on |
3 |
| Mastra Workflow |
n/a (consumes from n8n trigger) |
localhost:3002 currently K20N0S-only; Tailscale-exposure recommended. src: REPORT §11 Q3 |
K20N0S |
3 |
| Slack |
n/a |
✓ cloud |
n/a |
5 |
Cross-stack gaps surfaced
- No native auto-pause-after-N-failures anywhere. Inngest has it; Pantheon stack must build it (PRD §3 ladder + Themis bridge polling
fleet_schedule_runs). Every executor in inventory leaves this to the SSOT layer.
- No feedback / reflection loop wired end-to-end. Mastra writes
reflection_text but no executor reads it back. No cadence-adaptation, no retry-policy-learning, no evidence-shape evolution. Pure forward telemetry today.
- Identity leakage on Routines. Per-operator-account model collapses fleet-agent identity into the human operator — commits/connectors appear as the human. Restricts Routines to
vcs_mutating: false work only (PRD §8 Q5). Cuts ~30% of would-be cloud schedules.
- n8n tenant-boundary weakness. mimir-n8n + pantheon-ops author same instance; multi-replica double-fire (CRIT) + silent-stall scheduler (CRIT, 2yr unresolved); namespace isolation between authoring agents is convention, not enforced.
- Cross-machine fire-once coordination is manual.
executor_machine field disambiguates which host runs which schedule, but there is no leader election / pg advisory lock built-in. Two Themis-equivalent executors on two Macs would double-fire. src: PRD §5.
Cross-stack strengths
- File→table SSOT pattern is proven.
fleet_tasks (Themis) + fleet_investigations (Themis-I) established the one-way bridge; fleet_schedules extends with one more parser, ~6-9h post-DDL. src: PRD §5 Option E.
- Rule-18 anti-overclaim hook designed-in at DB layer. Postgres CHECK constraint
(outcome != 'success' OR verification_evidence IS NOT NULL) is unrepresentable, not just flagged. Mastra writeRun step is the application-side enforcer. src: PRD §6, REPORT §7.
- Multiple executors, single SSOT.
fleet_schedules.executor enum (launchd, routine, n8n, github-actions) lets per-schedule executor choice be authored in YAML without changing protocol. Defensible against any single executor's failure mode.
- launchd wake-catchup is best-in-class for laptop fleet. Mac Mini K20N0S no-sleep + MacBook wake-catchup means missed-once semantics, never silent drop. Cron does not match this on macOS.
- Slack workspace isolation gives multi-tenant for free. Per-app-per-workspace rate budget means slack-boas fleet-ops noise cannot DOS slack-viska client schedules. Cheap cross-tenant boundary.
- Mastra OTel coverage is first-class. 11 first-party exporters (Langfuse, Datadog, etc.) give per-step LLM/Tool/Workflow/Step spans on the analysis path — strongest observability surface in the stack.
Architecture invariants worth preserving in any adoption decision
- Rule-18
verification_evidence CHECK constraint must remain enforceable. Any new executor MUST surface entity_ids / commit_shas / file_paths / api_acks / slack_message_ts in a deterministic step, not LLM-decided. Bare HTTP 200 is not evidence. src: REPORT §7.2.
- File→table SSOT via Themis is non-negotiable. Authoring surface is
{repo}/schedules/*.yaml; query surface is fleet_schedules Postgres. New executors must mirror through Themis, never bypass to write fleet_schedules directly. src: PRD §G7, rule 08 / 10.
- Athena ratification path for cross-agent + fleet-wide schedules.
requires_governance_ratification: true + ratified_by + ratified_at frontmatter; Themis refuses to mirror without it. Pre-approval template pool reduces friction. src: PRD §3 / decisions--scheduled-tasks-cross-agent-ratification.md (constellation-hermes-008).
- Quiet-hours + war-room-freeze pause primitives.
paused_by_quiet_hours, paused_by_war_room, paused_by_owner, paused_by_failure as independent OR-flags; per-schedule freeze_exempt: true opt-in for probes. src: PRD §8 Q3, decisions--scheduled-tasks-freeze-exemption.md (constellation-hermes-010).
- Mastra Agent stays analyze-only — no side-effect tools. Tool-constraint forces deterministic write + post steps, immune to LLM forgetting to persist. Any successor analysis layer must preserve this sandbox shape. src: REPORT §5.1.
update_scheduled_task MCP tool is FORBIDDEN fleet-wide. Schedule mutation flows through file-edit → Themis mirror, never from inside a running prompt. Applies to Desktop, Routines, and any candidate executor with self-modifying primitives. src: PRD §8 Q6.
- Executor enum is the extension point. Adding a new executor (e.g., nous-hermes-agent) means extending
fleet_schedules.executor CHECK constraint enum + Themis parser case + one mastra-bridge endpoint OR direct write contract. NOT a protocol rewrite. src: REPORT §10.