
Debugging Signals Pipeline
FreeEfficiently debug your signals pipeline locally.
Free · Opens the source repo
What Debugging Signals Pipeline does
The Debugging Signals Pipeline skill provides a comprehensive toolkit for developers needing to debug the signals pipeline within the PostHog ecosystem. This skill enables users to emit test signals, monitor workflows, and inspect logs and containers, ensuring that signals are processed correctly from start to finish. It is particularly useful for diagnosing issues when signals fail to reach their intended destination or when workflows do not complete as expected.
Users can start by emitting test signals from predefined fixtures, allowing for quick validation of the pipeline's functionality. The skill includes commands to clean up stale signal data, which is crucial for avoiding phantom matches in reports. Additionally, it features monitoring capabilities for Temporal workflows via a REST API, enabling users to list recent workflows and inspect their execution history, making it easier to identify where failures occur.
For those needing to troubleshoot further, the skill allows access to sandbox agent logs stored in object storage, providing insights into the status and errors of task runs. Users can also inspect Docker containers running the sandbox, offering a view into the processes and logs that can reveal issues with signal processing. With built-in guidance for common failures, such as stale embeddings in ClickHouse, this skill equips developers with the knowledge to resolve issues efficiently.
Overall, this skill is designed for developers working with PostHog's signals pipeline, offering a structured approach to debugging and ensuring that signals are handled correctly throughout the workflow.
When to use it
Use this skill when you encounter issues with signals not reaching the inbox or when workflows fail to complete as expected.
When not to use it
This skill is not suitable for general-purpose debugging outside of the PostHog signals pipeline or for non-PostHog related applications.
What you can build with it
Testing Signal Emissions
Emit test signals from fixtures to validate the pipeline's functionality and ensure correct signal processing.
Monitoring Workflow Execution
Use the skill to monitor Temporal workflows and inspect their execution history for troubleshooting.
Inspecting Logs and Containers
Access sandbox agent logs and inspect Docker containers to diagnose issues with signal processing.
How to install Debugging Signals Pipeline
View source1. Install with the skills CLI
npx skills add posthog/posthog/debugging-signals-pipeline --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by posthogDebugging the signals pipeline
Pipeline flow
emit_signals_from_fixture
→ signal-emitter (Temporal workflow)
→ buffer-signals (batches signals, 5s flush timer)
→ safety_filter_activity
→ flush_signals_to_s3_activity
→ signal_with_start_grouping_v2_activity
→ team-signal-grouping-v2 (30s batch collect window)
→ read_signals_from_s3_activity
→ get_embedding_activity + generate_search_queries_activity
→ run_signal_semantic_search_activity
→ match_signal_to_report_activity
→ assign_and_emit_signal_activity
→ wait_for_signal_in_clickhouse_activity
→ (if new report) signal-report-summary
→ fetch_signals_for_report_activity
→ report_safety_judge_activity
→ select_repository_activity (spawns Docker sandbox)
Emitting test signals
# Emit a single signal from the Zendesk fixture at offset 26
DEBUG=1 python manage.py emit_signals_from_fixture --type zendesk --team-id 1 --offset 26 --limit 1
# Clean up all signal data before re-emitting (avoids stale matches)
DEBUG=1 python manage.py cleanup_signals --team-id 1 --yes
# Check pipeline status
python manage.py signal_pipeline_status --team-id 1 --wait --expected-signals 1 --poll-interval 10
Always clean up before re-emitting to avoid stale embeddings causing phantom report matches.
Monitoring Temporal workflows
The Temporal UI runs at http://localhost:8081. The REST API is useful for scripted inspection.
List recent workflows
curl -s 'http://localhost:8081/api/v1/namespaces/default/workflows?query=ORDER+BY+StartTime+DESC&maximumPageSize=15' \
| python3 -c "
import sys, json
for wf in json.load(sys.stdin).get('executions', []):
info = wf['execution']
status = wf['status'].replace('WORKFLOW_EXECUTION_STATUS_', '')
print(f'{wf[\"startTime\"][:19]} {status:20s} {wf[\"type\"][\"name\"]:35s} {info[\"workflowId\"][:90]}')
"
Inspect workflow history
WF_ID="buffer-signals-1" # or team-signal-grouping-v2-1, signals-report:1:<uuid>
curl -s "http://localhost:8081/api/v1/namespaces/default/workflows/$WF_ID/history?maximumPageSize=200" \
| python3 -c "
import sys, json
for event in json.load(sys.stdin).get('history', {}).get('events', []):
etype = event['eventType'].replace('EVENT_TYPE_', '')
etime = event['eventTime'][:19]
details = ''
for key, attrs in event.items():
if key.endswith('Attributes') and isinstance(attrs, dict):
if 'activityType' in attrs: details = attrs['activityType'].get('name', '')
elif 'signalName' in attrs: details = f'signal: {attrs[\"signalName\"]}'
elif 'startToFireTimeout' in attrs: details = f'timer: {attrs[\"startToFireTimeout\"]}'
elif 'failure' in attrs: details = f'FAILED: {attrs[\"failure\"].get(\"message\", \"\")[:200]}'
if details: print(f' {etime} {etype:50s} {details}')
"
Inspect a previous run (continued-as-new)
When a workflow has continued-as-new, use the execution.runId query param:
curl -s "http://localhost:8081/api/v1/namespaces/default/workflows/$WF_ID/history?execution.runId=<run-id>&maximumPageSize=200"
Reading sandbox agent logs
Agent logs are stored in object storage (MinIO locally) as JSONL files.
The log URL is on the TaskRun model.
# In Django shell (python manage.py shell)
from products.tasks.backend.models import TaskRun
from posthog.storage import object_storage
# Find the most recent task run
run = TaskRun.objects.order_by("-created_at").first()
print(f"status: {run.status}, error: {run.error_message}")
print(f"log_url: {run.log_url}")
# Read the log
content = object_storage.read(run.log_url, missing_ok=True)
# Print last 3000 chars (most useful — shows what happened before failure)
print(content[-3000:])
The log is JSONL with entries like:
{
"type": "notification",
"timestamp": "...",
"notification": { "jsonrpc": "2.0", "method": "_posthog/console", "params": { "level": "debug", "message": "..." } }
}
Key things to look for in the log tail:
- agentsh network events —
DENYentries show blocked network calls _posthog/progressevents — show which setup step the sandbox reached_posthog/consoledebug messages — show sandbox provisioning, cloning, agent startup
Inspecting Docker sandbox containers
# List running sandbox containers
docker ps --filter "name=task-sandbox" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
# See processes inside a running sandbox
docker exec <container-name> ps aux
# Read the agent-server log inside the container (while it's still running)
docker exec <container-name> cat /tmp/agent-server.log
The container is named task-sandbox-<task-id>-<random> and uses the posthog-sandbox-base image.
Containers are ephemeral — they're removed after the task run completes, so inspect while running.
Common failures
SignalReport matching query does not exist
The assign_and_emit_signal_activity tried to assign a signal to a report that doesn't exist.
Usually caused by stale embeddings in ClickHouse after a cleanup_signals that failed to delete them.
Root cause: CLICKHOUSE_DATABASE not set in .env. The cleanup command uses sync_execute
which connects to the CLICKHOUSE_DATABASE (defaults to default), but the embedding tables
live in the posthog database.
Fix: Add CLICKHOUSE_DATABASE=posthog to .env and restart workers.
Manual cleanup of stale embeddings:
curl -s 'http://localhost:8123/' --data-binary \
"ALTER TABLE posthog.sharded_posthog_document_embeddings_text_embedding_3_small_1536 DELETE WHERE product = 'signals' AND team_id = 1 SETTINGS mutations_sync = 1"
Verify embeddings are clean:
curl -s 'http://localhost:8123/' --data-binary \
"SELECT count() FROM posthog.sharded_posthog_document_embeddings_text_embedding_3_small_1536 WHERE team_id = 1 AND product = 'signals'"
Run timed out due to inactivity on select_repository_activity
The sandbox Claude agent went idle for longer than TASKS_INACTIVITY_TIMEOUT_SECONDS. When unset
this falls back to a 2 hour timeout — set TASKS_INACTIVITY_TIMEOUT_SECONDS=30 locally to force fast failures.
Diagnosing: Read the agent log from object storage (see above). Check the tail for:
- agentsh network denials —
DENY host.docker.internalmeans the MCP server URL is blocked by the sandbox network policy. TheSIGNALS_REPO_DISCOVERYenvironment's domain allowlist doesn't includehost.docker.internal. - No log content at all — sandbox failed to start, check Docker container logs.
- Claude API errors — check if
ANTHROPIC_API_KEYis valid.
buffer-signals sits idle, never receives signals
The signal-emitter completed but buffer-signals never got the submit_signal.
This happens when the emitter sent the signal to a previous buffer run that then continued-as-new,
and the new run started fresh without the pending signal. Re-emit the signal.
ClickHouse embedding tables "not found" during cleanup
The tables exist in the posthog database but sync_execute queries the default database.
# Verify tables exist
curl -s 'http://localhost:8123/' --data-binary "SHOW TABLES FROM posthog LIKE '%embed%'"
# Check current CLICKHOUSE_DATABASE setting
grep CLICKHOUSE_DATABASE .env
Useful management commands
| Command | Purpose |
|---|---|
emit_signals_from_fixture | Emit test signals from JSON fixtures |
DEBUG=1 cleanup_signals --team-id N --yes | Delete all signal data and terminate workflows |
signal_pipeline_status --team-id N --wait | Wait for pipeline to finish processing |
list_signal_reports --team-id N --signals --json | Inspect grouping results |
ingest_signals_json <file> --team-id N | Ingest pre-processed signals from JSON |
ingest_report_json <file> --team-id N | Seed a pre-researched report (skip sandbox) |
Key file locations
- Pipeline workflow definitions:
products/signals/backend/temporal/ - Buffer workflow:
products/signals/backend/temporal/buffer.py - Grouping workflow:
products/signals/backend/temporal/grouping_v2.py - Report summary workflow:
products/signals/backend/temporal/summary.py - Docker sandbox implementation:
products/tasks/backend/logic/services/docker_sandbox.py - Sandbox Dockerfiles:
products/tasks/backend/sandbox/images/ - Agent log polling:
products/tasks/backend/logic/services/custom_prompt_internals.py - Cleanup command:
products/signals/backend/management/commands/cleanup_signals.py - Management command docs:
products/signals/backend/management/CLAUDE.md
Frequently asked questions about Debugging Signals Pipeline
Similar skills
Agent Host Debug Logs
Analyze Agent Host debug logs for deeper insights.
Code OSS Dev - Launch + Debug
Launch and debug Code OSS with isolated profiles.
Phoenix CLI
Debug LLM applications with structured analysis tools.
Power Automate Debugging
Diagnose and fix Power Automate flow errors effectively.
Arize Trace
Inspect and export traces for LLM applications.
Runtime Behavior Probe
Investigate real runtime behavior with precision.
