Operations

GitHub webhook read model

GitHub webhook read model

  • Status: active architecture and operator reference
  • Owner: ClawSweeper dashboard and queue maintainers
  • Source of truth: dashboard/github-webhook-read-model.ts, dashboard/worker.ts, and dashboard/exact-review-queue.ts
  • Last verified: this document's repository revision
  • Update when: ingress coverage, App subscriptions, snapshot schema, TTLs, repair policy, or consumer safety boundaries change

The queue Durable Object materializes signed GitHub App deliveries into a bounded, freshness-labelled read model. It serves planning, discovery, hydration, and dashboard reads through publisher-HMAC endpoints. Each response says which webhook watermark it represents, whether the required event class has ever been observed, and whether the object is stale.

#Required GitHub App event subscriptions

The deployed GitHub App must deliver all of these repository events:

SubscriptionMaterialized state
IssuesFull issue lifecycle, labels, lock/state, assignees, milestones, and counts
Pull requestsPR lifecycle, head/base, draft/state, labels, and counts
Issue commentsCreated, edited, and deleted comments, including ordinary non-command comments
Pull request reviewsSubmitted, edited, and dismissed review activity
Pull request review commentsCreated, edited, and deleted inline review comments
Workflow runsCurrent and recently completed workflow-run state
Workflow jobsCurrent job and step state
Check runs and check suitesCurrent check state associated with delivered commits

Subscription readiness is detected per event class. A class remains unavailable until at least one signed delivery of that exact class has arrived. After the 30-minute probe window, consumers continue their live poll and emit one structured github_read_model_degraded line with reason=never_observed; enabling a related event does not satisfy the missing class.

#Stored schema and bounds

The Durable Object assigns one monotonic repository-wide watermark to every unique GitHub delivery GUID. Per-object rows retain the source updated_at, delivery GUID, object watermark, receipt time, and normalized JSON. Older or duplicate deliveries can advance the global observation watermark but cannot replace a newer object row.

  • One item row per repository and issue/PR number.
  • Comments ordered by numeric ID, including deletion tombstones; at most 500 rows per item and 64 KiB per comment body.
  • Reviews and inline review comments stored separately with tombstones, counts, and a stable activity digest; at most 500 rows per item.
  • Workflow runs, jobs, check runs, and check suites; at most 1,000 current/recent objects.
  • Delivery GUID receipts retained for 30 days.

Item snapshots are stale after 15 minutes, comment and review collections after 30 minutes, and workflow state after 5 minutes. Placeholder discovery additionally requires a successful repair census within 6 hours. Missing, stale, incomplete, unsubscribed, or gap-detected snapshots fall back to GitHub and repair the Durable Object before later reads reuse it.

#Read and repair endpoints

Publisher-authenticated POST routes live below /internal/state/github-read-model/: item, comments, activity, workflows, placeholders, and repair. Authentication uses the same body HMAC as the other automated queue data-plane routes. Responses carry watermark, per-class subscription state, freshness, and usable; callers must not infer freshness from a successful HTTP response alone.

The migrated consumers are exact-item review planning, dashboard workflow-run/job health, review-placeholder discovery, and repair-loop comment hydration in the comment router. Each retains a bounded repair poll. The dashboard's composed live snapshot TTL is 60 seconds; webhook workflow state is independently capped at 5 minutes.

Workflow rows alone never establish completeness. Dashboard run snapshots require a persisted successful bounded run census, and job snapshots require fresh complete coverage for each run before that run can avoid its live job poll. Census repair removes older rows absent from the authoritative result while preserving webhook rows received after the census began.

Each workflow-run row also exposes its last delivery-or-poll confirmation time. Before an over-threshold queued row with an expired confirmation can affect operational health, the dashboard re-reads that exact run from GitHub. Each 20-second status refresh checks at most the ten oldest stale rows, which keeps the work to two five-way request waves. Unconfirmed rows omitted from that batch are excluded from queue pressure and make health unknown; later refreshes continue with the next-oldest rows. Structured github_read_model_workflow_run_revalidation_batch telemetry records the batch limit, selected count, and omitted count. A live active run refreshes the row; a completed or missing run is evicted with github_read_model_workflow_run_evicted telemetry. If exact verification fails, operational health is unknown rather than reporting an unconfirmed queued run. The eviction is bounded by the verification start time so a newer webhook delivery cannot be deleted by the repair. Rows beyond the existing 24-hour zombie boundary remain separately observable without exact rechecks.

Publisher-HMAC remains the normal automated read credential. Exact-review planning intentionally does not receive that shared secret; its item read instead uses the already-issued full lease tuple as a scoped capability. The queue accepts that capability only for the tuple's live leased repository and item, and it cannot write or repair the read model.

#Hard safety boundary

This read model never serves apply mutation guards, the two-sided lease check, or any post-mutation verification. Apply binds those reads to a LiveReadGeneration, which bypasses the webhook snapshot; explicit final-guard bypass reads always reach GitHub. Placeholder deletion likewise re-reads the closed item and exact comment live immediately before deletion. Webhook state is asynchronous and lossy, so it is evidence for planning and observation—not authority to mutate GitHub.

OpenClaw Bay is unaffected. It remains an observer-only projection with the same public fields and no queue, workflow, GitHub, recovery, deploy, or rollback actions.