Best for
- Use when: 'restart gateway', 'gateway status', 'is gateway healthy', 'push to gateway', 'gateway not responding', 'telegram not working', 'messages not going through', 'gateway stuck', 'gateway debug', 'check gateway',…
joelhooks/joelclaw/skills/gateway/SKILL.md
Operate the joelclaw gateway daemon — the always-on pi session that receives events, notifications, and messages. Use the joelclaw CLI for ALL gateway operations. Use when: 'restart gateway', 'gateway status', 'is gateway healthy', 'push to gateway', 'gateway not responding', 'telegram not working', 'messages not going through', 'gateway stuck', 'gateway debug', 'check gateway', 'drain queue', 'test gateway', 'stream events', or any task involving the gateway daemon.
Decision brief
The gateway daemon is the always-on pi session that receives events from Inngest functions, Telegram, and webhooks. It's the system's notification and communication layer.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/joelhooks/joelclaw --skill "skills/gateway"Inspect the Agent Skill "gateway" from https://github.com/joelhooks/joelclaw/blob/aedcffe3bd3d8aab78522f54fa405a4882e31560/skills/gateway/SKILL.md at commit aedcffe3bd3d8aab78522f54fa405a4882e31560. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
joelclaw gateway restart is the canonical restart. It kills the process, cleans Redis state, re-enables com.joel.gateway if launchd disabled it, waits for launchd to respawn, records the operator restart marker, and prints the doctor summary. joelclaw gateway enable is the direc…
Use the CLI. Do not hand-edit /.joelclaw/scripts/gateway-start.sh unless the CLI is broken.
Substrate precheck first (avoid chasing secondary gateway symptoms):
joelclaw gateway status now distinguishes:
Gateway rolling context refresh is useful only when it stays scoped.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 63 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
The gateway daemon is the always-on pi session that receives events from Inngest functions, Telegram, and webhooks. It's the system's notification and communication layer.
Rule: Always use joelclaw gateway CLI. Never use launchctl, curl, or log file grep directly.
joelclaw gateway doctor # PASS/FAIL process, live source tree, Redis, adapter, and poller
joelclaw gateway doctor --live # Full notify path; requires Telegram platformMessageId
joelclaw gateway status # Daemon availability, runtime mode, session pressure, Redis health
joelclaw gateway restart # Roll daemon, then print the doctor summary
joelclaw gateway enable # Re-enable launch agent + start daemon
joelclaw gateway test # Push test event, verify delivery (Redis bridge path)
joelclaw gateway push --type <type> [--payload JSON] # Push an event to all sessions
joelclaw gateway events # Peek at pending events per session
joelclaw gateway drain # Clear all event queues
joelclaw gateway stream # NDJSON stream of all gateway events (ADR-0058)
joelclaw gateway channel {list|status|enable|disable} # Enable/disable runtime channels
joelclaw gateway behavior {add|list|promote|remove|apply|stats} # ADR-0211 behavior control plane
joelclaw gateway restart is the canonical restart. It kills the process, cleans Redis state, re-enables com.joel.gateway if launchd disabled it, waits for launchd to respawn, records the operator restart marker, and prints the doctor summary. joelclaw gateway enable is the direct recovery path when launchd drift disabled the service. Never use launchctl bootout/bootstrap directly.
The live daemon executes source from the main joelclaw working tree. Never carry gateway-touching WIP there. Use a separate Git worktree so a daemon crash cannot relaunch half-edited code. gateway doctor makes a running daemon plus dirty gateway runtime source a loud failure. Its --live proof requires a real Telegram platformMessageId; confirmed telemetry without that ID is not delivery proof.
Use the CLI. Do not hand-edit ~/.joelclaw/scripts/gateway-start.sh unless the CLI is broken.
joelclaw gateway channel list
joelclaw gateway channel disable discord --restart
joelclaw gateway channel enable discord --restart
joelclaw gateway channel status discord
The CLI blanks/restores the channel env assignments in ~/.joelclaw/scripts/gateway-start.sh, writes a /tmp/joelclaw/gateway-start.sh.* backup before changes, and can restart the gateway when --restart is present. Telegram disable requires --force because it is a primary operator channel. Do not revoke or delete stored secrets for a temporary disable.
Expected disabled Discord state: component disabled; channel configured:false, started:false, ready:false, botUserId:null; health entry status:"disabled".
Substrate precheck first (avoid chasing secondary gateway symptoms):
colima status --json
kubectl get nodes -o wide
kubectl get pods -n joelclaw redis-0 inngest-0
If Colima is down or node/core pods are not healthy, recover substrate before gateway operations.
Run in order, stop at first failure:
joelclaw gateway doctor # 1. Process, source, Redis, adapter, poller
joelclaw gateway doctor --live # 2. Real Telegram delivery with platformMessageId
joelclaw gateway diagnose # 3. Deep evidence when doctor fails
joelclaw gateway restart # 4. Restart and print doctor summary
If joelclaw gateway status shows pending > 0 on sessions, the agent is mid-stream or stuck. If it persists after a minute, restart.
joelclaw gateway status now distinguishes:
mode: normal — Redis bridge healthymode: redis_degraded — daemon/channels/session available, but Redis-backed capabilities are degradedWhen mode=redis_degraded:
joelclaw gateway test validates the Redis bridge path, so expect joelclaw gateway diagnose to skip that layer intentionallyjoelclaw gateway diagnose to see the degraded capability list and session pressure fieldsDo not report redis_degraded as “gateway down” unless process/session health is also failing.
Gateway rolling context refresh is useful only when it stays scoped.
Hard rule:
Why:
context-refresh injection poisons the live gateway session even when joelclaw gateway status still reports healthyIf Joel says the gateway session feels "fucked" while health checks look green, inspect the gateway session transcript for hidden context-refresh / gateway-recovery / memory-recall messages before trusting the CLI summary.
joelclaw gateway status / joelclaw gateway diagnose now expose session-pressure specifics instead of just a coarse health word:
compact at 65% ... / rotate at 75% ... / rotate immediately)active / warm / total)context_usage, context_ceiling, compaction_gap, session_age)The daemon emits OTEL under daemon.session-pressure (session_pressure.alert.suppressed|failed). Session-pressure states do not page Telegram; rotation/compaction pressure stays in status/diagnose/OTEL because it is gateway maintenance, not Joel action.
Idle maintenance is autonomous for time-based pressure:
AgentSession.newSession(), gateway writes /tmp/joelclaw/gateway.force-new-session.json and exits cleanly so launchd restarts into a fresh SessionManager, then injects the compression summary as hidden context before the next inbound turndaemon.maintenance.started|completed|failed telemetry as turn-bound maintenanceFor direct human turns across Telegram, Discord, iMessage, and Slack invoke paths, the latest message now wins.
Runtime contract:
1.5s batching window before dispatchsession.abort() on the stale turnjoelclaw gateway status exposes supersession plus supersession.batchingjoelclaw gateway diagnose adds an interruptibility layer with supersession and batching detailsPassive intel / background event routes are excluded from this path.
Telegram operator actions now get trace ids and explicit lifecycle tracking.
Current covered paths:
cmd:* command-menu callbacksworktree:* callbackspitch:* ADR pitch callbacks/stop, /esc, and /kill commandsGateway now tracks kind=callback|command plus ack/dispatched/completed/failed/timed_out state for those paths, exposes canonical operatorTracing in joelclaw gateway status (with callbackTracing kept as a compatibility alias), and adds an operator-tracing layer in joelclaw gateway diagnose.
Queued Telegram agent commands now keep their trace id through downstream gateway execution, complete on the real turn completion path, and fail on prompt error / assistant error / supersession instead of lying at enqueue time. Agent-backed command traces use a longer timeout window than simple callback ack paths.
External callback routes now have a Redis trace-result handoff too: the gateway leaves routed callbacks active, downstream consumers can publish completed / failed back with the same traceId, and the in-tree Restate Telegram route is wired to close traces on real downstream resolution.
Still open: any out-of-tree external callback consumer that doesn't adopt the handoff will still timeout as untracked work.
joelclaw gateway status now exposes a canonical channels surface plus summarized channelHealth for Telegram, Discord, iMessage, and Slack. joelclaw gateway diagnose adds a channel-health layer so owner/passive/fallback and half-dead channel states are visible before a full outage.
Current rank-6 behavior also sends immediate degrade/recover alerts from the daemon, emits OTEL under daemon.channel-health, respects joelclaw gateway known-issues mute state so known flaky channels stop crying wolf, and now tracks guarded heal policy state per channel.
channelHealth.healing now shows restart/manual policy, degraded streaks, cooldown, attempt counts, last result, plus manualRepairRequired, manualRepairSummary, and manualRepairCommands when the watchdog cannot fix the channel itself. gateway diagnose adds channel-healing, spells out manual operator repair steps, and the watchdog can attempt guarded restarts for restart-eligible degraded channels while leaving ownership/lease conflicts as manual/operator work.
Telegram retrying getUpdates conflicts no longer read as healthy fallback: when polling is down and only retrying, the ownership contract degrades visibly in /health, gateway status, and gateway diagnose.
Low-signal operator-spam guardrails now also apply:
restate / restate/* sources as automation for batching, so successful queue-dispatch DAG completions don’t hit the live gateway session immediatelytest.gateway-e2e from operator delivery by defaultslack.signal.received) instead of bypassing relay policypackages/gateway/src/operator-relay.ts as the single heuristics surface for normalize → score → correlate → route across Slack/email signalsvip.email.received after the VIP pipeline delivers its direct Telegram brief so the gateway keeps correlation context without sending a duplicate alert, while allowing lower-signal email/Slack items to batch into a correlated digest by project/contact/conversation keysHEARTBEAT_OKKnowledge Watchdog Alert messages always; degraded turn-write accounting is system maintenance, not operator workclaude-opus-4-6, floor fallbackTimeoutMs to 240000; 120000 is now treated as stale and too aggressive for real Opus TTFTmessage_end events so aborted turns cannot poison the next turn's latency/fallback monitoringmodel_fallback.decision) with reason buckets / probe counts / probe backoff so OTEL can separate real provider sickness from a noisy control loopMuted degraded channels now also flip to manual with the known-issue reason surfaced as repair guidance, instead of falsely advertising a restart policy that the watchdog will skip while muted.
Still not done: stricter cross-channel ownership enforcement and richer/native repair automation beyond CLI-guided manual steps.
Gateway runtime now enforces two operator-visible guardrails:
daemon.guardrails:guardrail.checkpoint.*git push where HEAD touched apps/web/ or root config (turbo.json, package.json, pnpm-lock.yaml)vercel ls --yes 2>&1 | head -10 after ~75sdaemon.guardrails:guardrail.deploy_verification.failedUse joelclaw gateway status to inspect live guardrails state, and joelclaw gateway diagnose when a checkpoint or deploy verification is active.
Gateway behavior is now explicit + deterministic:
joelclaw:gateway:behavior:contract)gateway_behavior_historyjoelclaw gateway behavior ...)Operator directives can be entered directly via CLI or in-channel using strict syntax:
KEEP: ...MORE: ...LESS: ...STOP: ...START: ...Gateway extension passively captures those lines and shells to joelclaw gateway behavior add ....
It does not write Redis or Typesense directly.
Daily review is advisory-only: candidates are generated by cron and must be promoted manually via joelclaw gateway behavior promote --id <candidate-id>.
Every Inngest function receives gateway via middleware (ADR-0035):
async ({ event, step, ...rest }) => {
const gateway = (rest as any).gateway as GatewayContext | undefined;
await step.run("notify", async () => {
if (!gateway) return;
await gateway.notify("my.event.type", {
message: "Human-readable summary",
data: "structured-payload",
});
});
}
| Method | Purpose | Routing |
|---|---|---|
gateway.progress(msg, extra?) | Pipeline/loop progress | Origin session + central gateway |
gateway.notify(type, payload?) | Notifications (task done, webhook) | Origin session + central gateway |
gateway.alert(msg, extra?) | System warnings (disk, service down) | Central gateway only |
Import: import type { GatewayContext } from "../middleware/gateway";
Always null-check gateway — functions can run without a gateway session.
For code outside the middleware:
import { pushGatewayEvent } from "./agent-loop/utils";
await pushGatewayEvent({
type: "video.downloaded",
source: "inngest/video-download",
payload: { message: "Downloaded: My Talk" },
originSession: event.data.originSession,
});
| Symptom | Likely Cause | Fix |
|---|---|---|
| Status shows healthy but messages don't arrive | Session stuck mid-stream on hung tool call | joelclaw gateway restart |
| Pending events growing on a session | Agent processing or blocked | Wait 1min, then joelclaw gateway restart |
| Telegram messages not delivered | HTML parsing error in response | Check joelclaw gateway status, restart |
| Telegram is spammed with raw inbound email | front.message.received relay gate too permissive or classifier drift | Check packages/gateway/src/operator-relay.ts; raw Front email should page only for production/security/money failures or human/project direct asks |
Telegram is spammed with meta system alerts (gateway.*, session pressure, Knowledge Watchdog, Slack channel_not_found) | Maintenance/check events escaped suppression | Keep direct watchdog/channel/system alerts out of Telegram; operator relay should classify meta system chatter as suppressed.meta-system-chatter |
| Slack passive firehose looks dead (mentions still work) | SLACK_ALLOWED_USER_ID not derived at startup | Ensure slack_user_token lease works; gateway-start.sh derives user id via auth.test, then joelclaw gateway restart |
| Important Slack channels are not being collected | SLACK_IMPORTANT_CHANNEL_IDS/SLACK_IMPORTANT_CHANNEL_NAMES missing or channel IDs drifted | Check private ~/.joelclaw/scripts/gateway-start.sh, restart gateway, and verify startup log shows importantChannelIds > 0 |
| Slack replies have no default target | SLACK_DEFAULT_CHANNEL_ID not derived at startup | Ensure slack_bot_token lease works; gateway-start.sh derives DM channel via conversations.open, then restart |
| Gateway restarts every few seconds | Crash loop — bad secret lease or code error | Check /tmp/joelclaw/gateway.err, fix cause |
| Redis connection failed | Redis pod down or Colima/k8s substrate down | Check colima status --json, then joelclaw status/kubectl for cluster health |
langfuse-cost optional dependency warning | Langfuse tracing dependency missing for pi extension runtime | Observability degradation only; do not treat as message-path blocker |
launchd (com.joel.gateway)
└─ gateway-start.sh (leases secrets, sets env)
└─ bun run daemon.ts
├─ createAgentSession() → headless pi session (reads SOUL.md)
├─ Redis channel (joelclaw:notify:gateway)
├─ Telegram channel (@JoelClawPandaBot)
├─ Slack channel (invoke on Joel/mentions; collect configured important channels)
├─ WebSocket (port 3018, for TUI attach)
├─ Command queue (serial — one prompt at a time)
└─ Heartbeat runner (periodic autonomous checks)
Gateway model standard: startup env sets PI_MODEL_PROVIDER=openai-codex and PI_MODEL=gpt-5.6-sol; Redis config key joelclaw:gateway:config should store model: "gpt-5.6-sol". Fallback remains openai-codex/gpt-5.4 so the fallback controller has a distinct lower-cost/known-good target. Cheap helper paths use openai-codex/gpt-5.4-mini; historical haiku command aliases remain but map to Codex mini. pi 0.73.0 exposes no Codex nano model.
The gateway reads ~/.pi/agent/ at boot for identity/prompt context (SOUL.md, AGENTS.md, MEMORY.md, daily log), but the gateway extension itself is context-local:
~/Code/joelhooks/joelclaw/pi/extensions/gateway/index.ts~/.joelclaw/gateway/.pi/extensions/gateway (symlink)~/.pi/agent/extensions/gateway globallyDaemon startup enforces this invariant and will fail if local extension is missing or a global gateway extension is detected.
This keeps gateway automation hooks out of normal interactive pi sessions.
| File | Purpose |
|---|---|
packages/gateway/src/daemon.ts | Daemon entry — session creation, channels, heartbeat |
packages/gateway/src/channels/redis.ts | Redis subscribe, drain, prompt build |
packages/gateway/src/channels/telegram.ts | Telegram bot channel |
packages/gateway/src/command-queue.ts | Serial FIFO queue → session.prompt() |
packages/gateway/src/heartbeat.ts | Periodic autonomous task runner |
packages/system-bus/src/inngest/middleware/gateway.ts | Middleware injecting gateway context |
packages/cli/src/commands/gateway.ts | CLI subcommands |
~/.joelclaw/scripts/gateway-start.sh | launchd start script |
/tmp/joelclaw/gateway.{log,err,pid} | Runtime logs and PID |
Frequently asked questions
The gateway daemon is the always-on pi session that receives events from Inngest functions, Telegram, and webhooks. It's the system's notification and communication layer.
The source record exposes this install command: npx skills add https://github.com/joelhooks/joelclaw --skill "skills/gateway". Inspect the command and pinned source before running it.
Alternatives
garrytan/gbrain
End-to-end discipline for turning any large data source (audio libraries, email takeouts, document corpora, chat exports, API dumps) into brain pages at scale. The lifecycle spine: SCHEMA → ACCESS → TRIAL → EVALUATE → IMPROVE → CODIFY → TEST → SKILLIFY → BULK → MONITOR. State is tracked in a durable JSON manifest (see MANIFEST-PATTERN.md) so any crash, session boundary, or subagent fan-out resumes from ground truth instead of memory.
alirezarezvani/claude-skills
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
dotnet/skills
Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing
vipshop/cache-dit
High-level guide for integrating a new DiT model into cache-dit: Cache (BlockAdapter/ForwardPattern), Context Parallelism, Tensor Parallelism, Text Encoder Parallelism (TE-P), VAE Parallelism (VAE-P), generate CLI, installation, testing workflow, and detailed references. Use when adding support for a new diffusion transformer model in cache-dit.