Skip to main content
← Back to list
01Issue
BugIn ProgressSwamp CLI
Assigneeskeeb

Relationships

#1591 swamp serve is telemetry-dark: scheduled workflow runs never reach /ingest, and the daemon never flushes

Opened by keeb · 8/11/2026

Summary

A long-lived swamp serve is telemetry-dark for its entire uptime. Workflow runs it executes — scheduled, webhook, and API-triggered — record no cli_invocation telemetry at all, and the daemon has no flush path of its own. swamp-club therefore receives zero events for work that demonstrably happened, which is what reset kneel's 32-day streak in #1590.

This is not a scoring or streak bug. swamp-club cannot credit a day it received no event for. The gap is entirely CLI-side.

Impact

  • Scheduled runs contribute nothing to score, streak, or the combat log.
  • An operative whose automation is the point of their setup looks idle.
  • The more correctly you run swamp (persistent serve, cron triggers), the less of your work is visible. #1590 is the first report; it will not be the last.

Root cause — three independent layers, all in serve

1. Serve never wires the workflow telemetry sink.

WorkflowRunDeps.telemetrySink is optional (src/libswamp/workflows/run.ts:287) and is bound in exactly one place: src/cli/commands/workflow_run.ts:323, the interactive swamp workflow run command. Serve's createWorkflowRunDeps (src/serve/deps.ts, returns at :281) never sets it, and telemetrySink appears nowhere in src/serve/. Every serve-side run goes through executeWorkflowWithLocks (src/serve/deps.ts:293) with the sink undefined, so the run produces no parent entry and no child method-invocation entries. There is nothing to flush, authed or not.

2. Recording and flushing exist only in the CLI wrapper.

TelemetryService is constructed at src/cli/mod.ts:1233, recordSuccess is called at :1602, and flushTelemetry at :1613 — all after cli.parse(args) returns. For swamp serve that instant is process exit. A daemon that never restarts never flushes, even for whatever it did spool.

3. The only record of a scheduled fire is an OTel span.

ScheduledExecutionService.executeWorkflow wraps each fire in withSpan("swamp.scheduled.fire", …) (src/libswamp/workflows/scheduled_execution.ts:441). That goes to the operator's own OTLP endpoint, not to swamp-club — the two pipelines are entirely separate (HttpTelemetrySender POSTs to <endpoint>/ingest, src/infrastructure/telemetry/http_telemetry_sender.ts:73). So the operator watches their workflow fire every 4h in their own Grafana while swamp-club sees silence, which makes the bug read as "the site lost my streak."

Evidence (from #1590, verified against the prod events mirror)

  • 11 consecutive successful scheduled runs, 2026-08-09 04:00 → 2026-08-11 00:00 UTC, each on the 4-hour boundary. Zero events reached swamp-club.
  • The reporter's own serve events carry whole-process durationMs (81s, 166s, 330s, 806s) with startedAtcompletedAt — one event per process, emitted at exit. His last one is 2026-08-09 05:06, before the systemd unit went persistent.
  • Across 230M events, cli_invocation.source{user, agent_skill} only — there is no scheduler or serve source, and no scheduled-run/workflow-run event type in the catalog.
  • Result: one missing UTC day (last event 2026-08-09 22:41:47Z, next 2026-08-11 01:49:10Z), streak 32 → 1.

Not the cause

  • Not OTEL env vars. OTEL_EXPORTER_OTLP_ENDPOINT and friends only affect withSpan/OTLP export under src/infrastructure/tracing/. They cannot divert or suppress the /ingest sender.
  • Not auth. The flush attaches x-api-key only when ~/.config/swamp/auth.json has credentials (src/cli/mod.ts:1200-1211), and the token is optional — a missing one changes attribution, not whether the send happens. --auth-mode none governs serve's inbound HTTP auth, not outbound telemetry credentials.
  • Not the cron cadence. 0 */4 * * * fires at 00:00 UTC every day; it could never skip a UTC day. Evenness isn't the problem — silence is.

Suggested fix

Two parts, and the second is the one that's easy to forget:

  1. Wire a telemetrySink into serve's createWorkflowRunDeps so scheduled, webhook, and API-triggered runs record parent + child invocations, with a source that distinguishes them from interactive use (the current cli_invocation.source enum has no value for this).
  2. Give the daemon a periodic flush loop. Serve cannot rely on process exit, so a sink alone just spools forever — and the 7-day hard retention in TelemetryService.cleanupOldTelemetry would eventually drop it.

Identity for the flush should come from the same place the CLI gets it: the user-level identity plus ~/.config/swamp/auth.json credentials the serve process already runs under.

Environment

  • swamp 20260804.201713.0-sha.c582ab91
  • Self-hosted swamp serve as a persistent systemd user service, --auth-mode none on loopback
  • Workflow with trigger.schedule: "0 */4 * * *"; /health reports scheduling.enabled: true
  • #1590 — the originating report (scheduled runs don't count toward the streak).
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 11 MOREREVIEW+ 1 MOREIMPLEMENTATION

In Progress

8/11/2026, 4:20:58 AM

Click a lifecycle step above to view its details.

03Sludge Pulse
keeb assigned keeb8/11/2026, 3:08:47 AM

Sign in to post a ripple.