Tasuku
Operations

Observability

Worker logs, Workflow status, queue metrics, health checks, and the live run stream.

There is no separate worker process to watch — every signal below comes from the one deployed Cloudflare Worker, its Durable Objects, Workflows, and queues.

Worker logs

bunx wrangler tail

observability is enabled in wrangler.jsonc with head_sampling_rate: 1, so every invocation is captured. Logs are structured; correlate them using the identifiers below rather than free text.

HTTP request log

Every HTTP request handled by the Worker emits one JSON log line, http_request, from server/src/http/middleware/logging.ts:

{
  "request_id": "3f2b1a9c4d5e6f708192a3b4c5d6e7f8",
  "cf_ray": "8a1b2c3d4e5f6789-SJC",
  "method": "GET",
  "route": "/api/v1/organizations/:organization_id/workflow-runs/:run_id",
  "status": 200,
  "duration_ms": 42,
  "colo": "SJC",
  "org_id": "…",
  "user_id": "…"
}

route is the Hono-matched path template (:organization_id, not the concrete id), so it can be aggregated across requests. org_id/user_id are only present once a request authenticates as an organization member — both are opaque ids, never emails or tokens. An uncaught error additionally logs a second line, http_request_failed (method, route, request_id, stack), from app.ts's error handler, before the standard { "error": { "code", "message" } } 500 envelope is returned; expected errors (ApiError, HTTPException) do not — they're routine 4xx/5xx outcomes, not failures.

Nothing logged ever includes the Authorization/Cookie header values, request/response bodies, or secret plaintext.

Correlation identifiers

Requests, workflow runs, and agent attempts each carry an id you can grep for across wrangler tail, the Workflow status API, and the dashboard:

  • HTTP request id (request_id above) — reuses an inbound X-Request-ID header when it matches ^[A-Za-z0-9._-]{1,128}$, otherwise mints one; echoed on every response's X-Request-ID header, including error responses;
  • cf_ray (Cloudflare's own edge request id) and colo (the serving data center), when Cloudflare supplies them;
  • GitHub delivery id / Slack event id / Linear delivery id;
  • organization and repository id;
  • workflow run and job id;
  • attempt id — also the RunAttemptWorkflow instance id (runId:attempt);
  • sandbox provider and lease id.

None of these include secret plaintext or raw webhook bodies.

Workflow instance status

Each RunAttemptWorkflow and SecretRotationWorkflow execution is inspectable by id:

bunx wrangler workflows instances describe tasuku-run-attempt <instance-id>
bunx wrangler workflows instances list tasuku-run-attempt

This shows the current step, retries, and any error a step raised — useful when an attempt is stuck rather than failed outright, since RunAttemptWorkflow steps retry independently (see Architecture).

Queue metrics and dead-letter queues

tasuku-inbox, tasuku-effects, and tasuku-review-batches each report backlog, throughput, and retry counts in the Cloudflare dashboard's Queues view, or via:

bunx wrangler queues list

A message that exhausts its max_retries (5, for every queue here) lands in that queue's dead-letter queue instead of being dropped. There are three: tasuku-inbox-dlq, tasuku-effects-dlq, and tasuku-review-batches-dlq, all declared in wrangler.jsonc; a non-empty DLQ means something is repeatedly failing (a Slack/Linear inbox item, an outbound GitHub/Slack/Linear effect, or a review batch) and needs manual inspection, not just a retry.

Health checks

GET /healthz
GET /readyz

/healthz reports that the Worker can answer HTTP at all. /readyz runs behind the same migrations-gate middleware as /api/* (so migrations are already applied by the time its handler runs), then pings D1 with SELECT 1: it returns 503 only when that ping fails, 200 otherwise. There is no separate "shutting down" state to report — Workers has no long-running process to drain. Route traffic only when /readyz succeeds; keep any external uptime check on /healthz so a transient D1 blip doesn't look like a full outage.

Live run stream

GET /api/v1/organizations/:organization_id/workflow-runs/:run_id/events/stream

The dashboard opens this as Server-Sent Events, backed by the run's RunEvents Durable Object: events are pushed only after the D1 write that produced them succeeds, the DO keeps a 500-event ring for reconnects, and it replays older events from D1 beyond that. A stream is capped at roughly 55 seconds before the server sends event: reconnect and the client reopens it — this is a Worker connection-duration detail, not a sign anything failed. Persisted D1 events remain the audit source if a client disconnects and never reconnects; the stream is a convenience, not the only copy. A polling fallback (?after=) remains available for clients that don't use SSE.

Use Troubleshooting for common failure symptoms and error codes.

On this page