Repository navigation
feat(statuspages): drag-and-drop resource reordering + dash conventions - #41
Merged
Merged
Conversation
Closes the audit gap: previously an operator who removed SP_ENCRYPTION_MASTER_KEY only saw the generic "encryption disabled" warning. Now the startup auto-migrate path also counts non-NULL config_private/settings_private rows and emits a separate WARN naming the count, so the silent failure mode (workers skipping every encrypted job) is loud at boot rather than mysterious at runtime.
The job runner walks one step of an escalation policy: loads the step+policy+incident, exits if the incident has been acked/snoozed/ resolved (belt-and-braces; the cancel sweep should already have killed the row), fans out to targets, emits incident.escalated, and on the last step of a repeat-eligible policy schedules the next cycle. Target dispatch: - connection target → enqueue a NotificationJob (re-uses per-channel formatting). - user / all_admins / schedule → resolve to user emails and send directly via the email service. V1 reality: email is the only cleanly per-user channel; richer per-user routing waits on a follow-up spec. Schedule failures (no users / not yet active) emit incident.escalation_failed but do not abort subsequent steps. The incidentUid key in the job config matches what jobsvc.CancelPendingForIncident looks for, so ack/snooze/resolve drops the rest of the policy without extra cancellation wiring. Includes: GetEscalationPolicyStep DB method (postgres + sqlite), incident.escalation_failed event type, GetEscalationPolicy now accepts empty orgUID for runtime callers that only know the policy UID, SetOnCallResolver seam to be wired by escalationruntime.
…e guard Hooking incident.created emission: resolve the policy (check's escalation_policy_uid, then group's, then nil) and schedule cycle 0 of its steps via ScheduleEscalationCycle. The cancel sweep keyed on incidentUid will drop them on ack/snooze/resolve with no extra wiring. Wired the on-call resolver as a closure in server.go so the escalation step job can call oncallschedules.Service.Resolve without an import cycle (jobtypes → oncallschedules would be one). DeletePolicy now returns 409 ESCALATION_POLICY_IN_USE when an open incident's check or group still references the policy — prevents the "pending escalation jobs reference a deleted policy" footgun.
Two integration tests with an in-memory sqlite DB: - ScheduleEscalationCycle persists three jobs at the right cumulative delays (startedAt + d0, + d0+d1, + d0+d1+d2), with the last one flagged IsLastStep so the next-cycle reschedule fires. - jobsvc.CancelPendingForIncident drops escalation_step jobs by matching incidentUid in the config — verifies the headline invariant that ack/snooze/resolve cancellation requires no extra wiring beyond the existing notification-job sweep. Also drops bun's `default:0` tag from EscalationPolicyStep.DelayMinutes: the tag was causing bun to omit non-zero int values from INSERT statements (every persisted step came back with delay=0 regardless of the Go value). The DB-level default in the migration is sufficient.
- /orgs/$org/escalation-policies — list of policies with repeat info. - /orgs/$org/escalation-policies/new — wizard: basics, steps with per-step delay and target list (user / schedule / connection / all_admins), repeat config. - /orgs/$org/escalation-policies/$slug — same shape as the wizard, loaded from the policy. Surfaces the 409 ESCALATION_POLICY_IN_USE error from delete via a try/catch alert. - API hooks: useEscalationPolicies, useEscalationPolicy, useCreateEscalationPolicy, useUpdateEscalationPolicy, useDeleteEscalationPolicy. - Sidebar: adds the entry under On-call, ArrowUpRight icon. - i18n: escalation namespace in en/fr/de/es; escalationPolicies key added to the nav namespace per locale. V1 frontend deliberately ships with the simpler layout (form rather than a draggable timeline visual). The data model and APIs back the richer UI; visual polish lands as a follow-up once shape stabilizes in real use.
…meline - Check create/update now accepts escalationPolicyUid (and clears via empty string on PATCH). Wired through CheckUpdate model into both postgres and sqlite UpdateCheck implementations. CheckResponse now surfaces the field so the dashboard can show what's assigned. - Incident detail page gets a dedicated EscalationTimelineCard that filters events by incident.escalated / incident.escalation_failed and renders one row per fired or failed step with the position and repeat cycle, separately from the generic event log. Group-level UI assignment and the keyEscalationThreshold rename (adaptive-resolution vocabulary cleanup) deferred — they're flagged in the spec's risks section and tracked separately.
Closes the v1 spec gap from the previous pass: - AnnotationCanvas: pointer-driven overlay over the captured screenshot with normalized [0,1] coordinates so drawings stay accurate when the preview is resized between draw and submit. - AnnotationToolbar: select / rect / arrow / text + undo. Tools render in red (#ef4444) at 3px stroke. - bakeAnnotations rasterizes the overlay onto a copy of the screenshot at submit time so the outgoing PNG ships with the markup baked in. - renderAnnotations is exported as a pure function — same code paths used for the live preview and the baked PNG. - bug-report.spec.ts: four scenarios — desktop happy path, keyboard shortcut, mobile event-triggered (toolbar hidden via sm:block), and feature flag off (icon hidden, shortcut no-op). - i18n: tool_select / tool_rect / tool_arrow / tool_text / undo for en/fr/de/es.
Send membership_request_new.html to all org admins on request create or re-open; send membership_request_decision.html to the requester on approve or reject. Failures are logged but never block the API call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…letion Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers: create / re-open / duplicate / unknown org; owner cancel and non-owner forbidden; approve creates membership and reject persists reason; admin re-approve of rejected row bypasses cooldown; cooldown window and cancellation re-request semantics; defensive guard against a stored unsafe auto-join regex. Tests caught a real bug: GetMembershipRequest used a non-existent 'mr.uid' alias instead of the bun-generated 'membership_request.uid' table reference (fixed in both postgres and sqlite stores). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routes the same handlers under both /orgs/:org/channels and /orgs/:org/connections. Identical request/response shapes, identical auth, identical errors — verified by TestChannelsAliasMatchesConnections. PR-1 of the channel/connection naming alignment. Subsequent PRs: - PR-2: dashboard hooks switch to /channels - PR-3: backend rename (IntegrationConnection → Channel) - PR-4: dashboard type rename (Connection → Channel) - PR-5 (deferred): drop /connections alias once external clients move - PR-6 (deferred): rename DB tables
Switches the five top-level hooks (useConnections, useConnection,
useCreateConnection, useUpdateConnection, useDeleteConnection) to call
/api/v1/orgs/$org/channels{,/uid}. The check-binding paths
(/checks/$check/connections) are unchanged — those aren't aliased yet,
they migrate in PR-3.
The Connection TS type stays on Connection here; PR-4 renames it.
…ynthesis, size rule Adds a docs/CLAUDE.md hard rule (500-line soft target, 800-line hard cap) with a 6-step split procedure, then applies it: every competitor analysis over 800 lines is reorganized into a per-topic subdirectory. - New: competitors/hyperping/ (6 files) — multi-region confirmation logic, outage-vs-incident model, /start heartbeat endpoint, auto Web Vitals, i18n at the data-model level. - Refreshed + split: competitors/betterstack/ (8 files, was 1004 lines) — adds depth on confirmation_period, recovery_period (flap-resets), 3-of-N region quorum, the four escalation step types (escalation, time_branching, metadata_branching, instructions), severities, ack/snooze model, status-page subscriber model. - Mechanical splits: competitors/pingdom/ (was 1235), uptimerobot/ (1068), gatus/ (879) — content preserved verbatim. - New: research/alerting-patterns.md — synthesis of 12 grouped patterns worth borrowing (recoveryPeriod, instructions step type, /start endpoint, status-page subscribers, multi-region quorum, HMAC-signed webhooks, three-mode maintenance, ...). Input for future specs, not a spec itself. - Indexes updated (README, full-list, comparison) to point at every new leaf file. No file remains over the 800-line hard cap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Work in progress on spec
specs/todos/2026-05-07-04-edit-on-route-convention-and-oncall-edit-page.md.
- specs/todos/2026-05-07-04-...: drafts the convention (editing always
navigates to a dedicated route, never opens a modal) and the on-call
schedule edit-page migration as the proving ground.
- web/dash0/CLAUDE.md: documents the convention so future contributors
see it immediately.
- web/dash0/src/components/oncall/on-call-schedule-form.tsx: extract the
shared multi-section form so create and edit routes can both consume it.
- web/dash0/src/routes/orgs/$org/on-call.new.tsx: switched to the extracted
form (~260 lines removed).
- web/dash0/src/routes/orgs/$org/on-call.{$slug,index}.tsx: prep for the
new edit route — Edit button targets and detail-page cleanup in flight.
- web/dash0/src/routes/orgs/$org/channels.index.tsx,
escalation-policies.index.tsx: align listing-page styling alongside.
- locales/{de,en,es,fr}/oncall.json: minor i18n adjustments matching the
form extraction.
Note: the new route file on-call.$slug.edit.tsx has not been created yet;
this commit captures the in-flight extraction. Finish the spec to
complete the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ally move resources The status-page section editor's up/down chevron buttons were no-ops because every resource (and every section) was created with position=0. The dashboard's swap logic exchanges position values between two adjacent rows, but swapping 0↔0 leaves the data unchanged; the list-by-position-then-created_at order then stays frozen on every refresh. - service.go: CreateSection and CreateResource now compute max(position)+1 when the request omits position, via two new helpers (resolveSectionPosition / resolveResourcePosition). Behavior when position IS supplied is unchanged. - db.Service + postgres.go + sqlite.go: new MaxStatusPageSectionPosition and MaxStatusPageResourcePosition methods returning COALESCE(MAX(position), 0). - migration 020 (postgres) / 019 (sqlite): one-time renumber of existing rows by (partition by parent ORDER BY created_at, uid). Idempotent on re-runs. - service_test.go: three regression tests pinning sequential-position assignment for resources + sections plus the swap-reorders-list contract. - slack_test.go: mock stubs for the two new db.Service methods. The frontend swap logic in status-pages.$statusPageUid.index.tsx is unchanged — it works correctly once positions are distinct. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The grey GripVertical dots on the left of each resource row were a
non-interactive visual affordance. They now act as a real drag handle:
click-and-drag (or keyboard with Space + arrow keys) to reorder resources
within a section. The existing up/down chevron buttons keep working.
Backend
- New POST /api/v1/orgs/$org/status-pages/$page/sections/$section/resources/reorder
accepting {uids: [...]} that renumbers in a single transaction.
- Service layer validates the supplied UID list is exactly the section's
current resource set (no missing, extra, or duplicate UIDs) before
applying — returns 422 with VALIDATION_ERROR otherwise.
- New db.Service method ReorderStatusPageResources using bun.RunInTx.
- Two regression tests pin the rewrites-positions-by-UID-order contract
and the mismatched-UID rejection.
Frontend
- @dnd-kit/core + @dnd-kit/sortable + @dnd-kit/utilities added.
- DndContext + SortableContext wraps each section's resource list with
PointerSensor (4px activation) and KeyboardSensor; closestCenter +
verticalListSortingStrategy.
- ResourceRow becomes useSortable; the GripVertical icon is the drag
handle (grab cursor, focus ring for keyboard) with an aria-label.
- onDragEnd computes arrayMove and calls useReorderResources with the
new UID order. Errors surface via toast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dnd-kit's drop animation interpolates from the dragged element's release position back to its underlying DOM position. We were only updating the list after the server roundtrip, so the dragged row's DOM slot was still the *old* index when the drop animation ran — the row briefly snapped back to the start position before re-rendering at the new one. useReorderResources now applies an optimistic update via onMutate: it patches the cached useStatusPage payload immediately, swapping the section's resources into the new order. By the time dnd-kit draws the drop animation, the underlying DOM is already at the destination slot, so the animation is a no-op visually. onError restores the snapshot; onSettled invalidates to reconcile with the server (no behavioral change, just moved from onSuccess so we always re-sync even on failure). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reuse the existing shared/check-picker component (server-side search via useChecks?q=, debounced, keyboard-navigable, exclude-uids support) so users can type to find a check in the Add-Check-to-Section dialog instead of scrolling the full list. - AddResourceDialog drops its `checks` prop (CheckPicker queries by itself) and tracks both the selected uid and label so the trigger button shows the chosen check's name without re-fetching. - SectionCard no longer needs `checks`, and the page no longer calls useChecks at all — the picker handles its own data. - Closing the dialog clears the selection so reopening starts fresh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…search
Distilled from the May-2026 Hyperping + BetterStack research pass
(docs/competitors/{hyperping,betterstack}/, docs/research/alerting-patterns.md)
into specs/todos/. Each spec is independently shippable; spec 04 is the
largest and explicitly multi-phase.
- 2026-05-08-01-validating-check-state.md: a transient `validating`
CheckStatus surfaces "we noticed a failure but the confirmation
hasn't fired yet" instead of leaving the dashboard green up to the
alert moment. Borrows BetterStack's status enum.
- 2026-05-08-02-time-based-confirmation-and-recovery-periods.md:
replace count-based incidentThreshold + recoveryThreshold with
wall-clock confirmationPeriod + recoveryPeriod (seconds, flap-reset
on recovery). One-shot migration; no second knob, no opt-in. Per
user direction: drop the count logic outright.
- 2026-05-08-03-severity-primitive-for-escalation-steps.md: a
per-org Severity resource bundles channel-types (email/sms/voice/
push/critical_push/slack/...) so an escalation step can "page
critical" instead of duplicating a step per channel. Default seed of
Low / Default / Critical per org.
- 2026-05-08-04-outage-vs-incident-split.md: split the conflated
incidents table into Outage (operational alert, raw, ack/snooze/
resolve) and Incident (customer-facing, editable timeline updates,
status-page-attached). Four phases: rename → introduce new type →
API cutover → auto-publish.
- 2026-05-08-05-on-call-multi-rotation-and-override-as-event.md:
one schedule contains N rotations (enabling follow-the-sun
natively), and overrides become events with isOverride=true
instead of a parallel resource class. Sequenced after the user's
in-flight spec-04 oncall edit page lands.
- Extract slugify() from status-page-form.tsx into web/dash0/src/lib/utils.ts so the new escalation-policies create form can reuse it. - escalation-policies.new.tsx: swap field order (Name first, Slug second), auto-derive the slug from the name until the user types into the slug field — slugManuallyEdited freezes the auto-fill so manual overrides aren't clobbered on subsequent name edits. - design-reference.tsx: document the pattern with a live NameSlugExample + the import snippet so future contributors copy the exact shape.
…rch-and-size-rule # Conflicts: # server/go.mod # server/go.sum
golangci-lint err113 rejects ad-hoc fmt.Errorf as dynamic errors. Promote the "resource not in section" message to a package-level sentinel (errResourceNotInSection) and wrap it with %w in both the postgres and sqlite ReorderStatusPageResources implementations. Behavior is unchanged — handler still surfaces a 422 on mismatched UIDs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes 8 Playwright failures that came from local-main features whose
tests were never run against origin's CI before this merge.
Backend
- escalationpolicies/handler.go was treating the URL `:org` slug as
the organization UID, which made every list query return [] and every
create POST 500 with an FK violation against organizations(uid).
Add Service.ResolveOrgUID and call it at the top of every handler
(List/Create/Get/Update/Delete). New ErrOrgNotFound surfaces as 404
ORGANIZATION_NOT_FOUND.
Dashboard
- routes/orgs/$org/organization.tsx: render null while AuthContext is
loading instead of redirecting `!user?.isAdmin` immediately. The
redirect was firing before the user payload landed, bouncing every
admin off the Members/Settings/Requests sub-pages.
- components/ui/card.tsx: CardTitle now renders as <h3>. Tests use
getByRole("heading", ...) and the previous <div> wasn't matched.
- routes/orgs/$org/checks.new.tsx: forward `data.labels` to the
CreateCheck request the same way the edit page does, so a label
added in the create form actually persists through the round-trip.
- components/shared/check-form.tsx: relax the slug validator to
3-50 chars (was 3-20). Backend already allows up to 50 — auto-
generated heartbeat slugs like "heartbeat-heartbeat-2" (21 chars)
were rejected on the edit page, blocking submission.
E2E
- e2e/membership-requests.spec.ts: change `page.goto("/no-org")` to
`page.goto("no-org")`. The leading slash was resolving against the
host, bypassing the `/dash0/` baseURL prefix.
- e2e/channels.spec.ts: webhook button locator was `/^webhook$/i` but
the button's accessible name is "Webhook Generic JSON webhook to any
HTTP endpoint" — switch to `/^webhook\b/i`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without the explicit association the Radix Checkbox (rendered as a <button>) didn't toggle on label-text clicks: the label-wrapping implicit association only reliably fires for native <input>. The channels e2e test toggles a channel by clicking its name (the realistic user gesture), so this dropped the click on the floor — connectionsDirty stayed false, the edit submit skipped the PUT /checks/$uid/connections call, and the binding never persisted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migration 001 declared the column with SQL DEFAULT 1 (sqlite) /
DEFAULT true (postgres). Combined with Bun's INSERT semantics — bool
fields with their zero value get omitted, leaving the SQL DEFAULT to
fill in — every newly-created channel ended up is_default=true even
when the request body explicitly sent {"isDefault": false}.
The downstream effect is real: the check-create handler auto-attaches
all default connections to every new check, so every channel ever
created via the dashboard was getting silently bound to every new
check. Confirmed by curl: POST /channels with isDefault:false comes
back with isDefault:true.
Aligning the SQL DEFAULT with the model's NewIntegrationConnection
default (false). Test mode is fresh-DB on every run, so this flips
behaviour for the e2e suite immediately. Existing channels in
deployed DBs already have whatever value was written; no data
migration needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
fix(check-form): always send connectionUids in edit submit
Drop the connectionsDirty optimization. The dirty flag was hiding
this very class of bug — a click that didn't actually toggle the
underlying state would leave dirty=false, the edit submit would
skip the PUT /connections call, and the binding state would silently
desync from what the user clicked.
Also wires the Notify-via Checkbox to its label via id+htmlFor so
clicking the channel name (a realistic user gesture) triggers the
toggle reliably — the implicit label/descendant association is
flaky for Radix's button-based Checkbox.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Followup to dropping the dirty optimization on connection toggling — the state setter was kept by mistake, breaking dash0's strict tsc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous pattern /\/channels\/[^/]+$/ matched /channels/new too, so
when the create-channel mutation lagged, waitForURL satisfied
immediately on the still-on-form URL. detailUrl.split('/').pop()
then returned 'new', and the later
`bindings.data.some(b => b.uid === 'new')` always failed.
Tightening to a UUID match forces a real wait for the post-create
navigation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
fclairamb
added a commit
that referenced
this pull request
Jun 23, 2026
🤖 I have created a release *beep* *boop* --- ## [0.1.0](v0.0.0...v0.1.0) (2026-06-22) ### Features * add check type registry, sample configs, notification senders, and observability integrations ([eea049e](eea049e)) * batch — Warning/Degraded statuses, SSL/Docker check upgrades, MCP OAuth 2.1, config-as-code ([#85](#85)) ([936d830](936d830)) * batch spec implementations (dashboard UI, jobs, notifications, SaaS, perf) ([#78](#78)) ([fe003e4](fe003e4)) * bulk feature integration (discovery, web push, badges, webhooks, and more) ([#73](#73)) ([a976856](a976856)) * custom user-agent, refresh checks, vite plugin switch, CI updates ([#1](#1)) ([cc00858](cc00858)) * documentation site served at /docs (co-located in web/docs) ([#88](#88)) ([27d9ccd](27d9ccd)) * email check frontend (spec 03) ([#30](#30)) ([aebd106](aebd106)) * email inbox foundation via JMAP (spec 01) ([#24](#24)) ([0f3b0c1](0f3b0c1)) * email passive checks (spec 02) ([#27](#27)) ([ba5536f](ba5536f)) * **entitlements:** trim to MaxSSOUsers + MaxChecksPerMinute + status page history fixes ([#50](#50)) ([a007de0](a007de0)) * **entitlements:** trim to MaxSSOUsers + MaxChecksPerMinute, enforce both ([#49](#49)) ([d25cc5f](d25cc5f)) * group incident correlation (spec 04 backend v1) ([#31](#31)) ([1883986](1883986)) * **observability:** instrument hot path + add bench harness ([#60](#60)) ([c6604cc](c6604cc)) * **oncall:** searchable timezone dropdown + sidebar logo polish ([#46](#46)) ([4396898](4396898)) * per-IP HTTP rate limiting and concurrency limiting ([#59](#59)) ([f9cc273](f9cc273)) * soften HTTP rate limits with bounded queues + request timeout ([#63](#63)) ([025501b](025501b)) * SolidPing — distributed uptime monitoring platform ([eef4383](eef4383)) * **statuspages:** drag-and-drop resource reordering + dash conventions ([#41](#41)) ([cf910d4](cf910d4)) ### Bug Fixes * **deps:** update dependency i18next to v26 ([#26](#26)) ([d5d1b7e](d5d1b7e)) * **deps:** update dependency lucide-react to v1 ([#28](#28)) ([cdc8e8c](cdc8e8c)) * **deps:** update dependency react-i18next to v17 ([#29](#29)) ([13135d8](13135d8)) * **deps:** update dependency recharts to v3.8.1 ([#14](#14)) ([5d95a3b](5d95a3b)) * **deps:** update go dependencies (non-major) ([#16](#16)) ([2450f13](2450f13)) * **deps:** update go dependencies (non-major) ([#19](#19)) ([f842310](f842310)) * **deps:** update go dependencies (non-major) ([#37](#37)) ([c020eca](c020eca)) * **deps:** update go dependencies (non-major) ([#44](#44)) ([ff0bcd0](ff0bcd0)) * **deps:** update go dependencies (non-major) ([#52](#52)) ([2e3bc28](2e3bc28)) * **deps:** update go dependencies (non-major) ([#69](#69)) ([a65ed4c](a65ed4c)) * **deps:** update go dependencies (non-major) ([#77](#77)) ([a0920ff](a0920ff)) * **deps:** update go dependencies (non-major) to v1.4.2 ([#86](#86)) ([1eb7504](1eb7504)) * **deps:** update module github.com/aws/aws-sdk-go-v2/config to v1.32.18 ([#72](#72)) ([6406537](6406537)) * **deps:** update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.101.0 ([#39](#39)) ([f9c9563](f9c9563)) * **deps:** update module github.com/go-webauthn/webauthn to v0.17.3 ([#45](#45)) ([593931a](593931a)) * **deps:** update module github.com/go-webauthn/webauthn to v0.17.4 ([#70](#70)) ([fca1c97](fca1c97)) * **deps:** update module github.com/ibm/sarama to v1.48.1 ([#47](#47)) ([93ccf3a](93ccf3a)) * **deps:** update module github.com/ibm/sarama to v1.48.2 ([#54](#54)) ([e84d868](e84d868)) * **deps:** update module github.com/ibm/sarama to v1.49.0 ([#64](#64)) ([fd61bd7](fd61bd7)) * **deps:** update module github.com/oapi-codegen/runtime to v1.4.1 ([#65](#65)) ([a3a3e9f](a3a3e9f)) * **deps:** update module github.com/prometheus/common to v0.67.5 ([#61](#61)) ([095dd6f](095dd6f)) * **deps:** update module github.com/slack-go/slack to v0.24.0 ([#75](#75)) ([6e86d5b](6e86d5b)) * **deps:** update module golang.org/x/sys to v0.44.0 ([#42](#42)) ([a245730](a245730)) * **deps:** update module golang.org/x/sys to v0.45.0 ([#67](#67)) ([49c3d90](49c3d90)) * **deps:** update module golang.org/x/term to v0.43.0 ([#43](#43)) ([4a934f1](4a934f1)) * **deps:** update module google.golang.org/grpc to v1.81.0 ([#36](#36)) ([1be183a](1be183a)) * **deps:** update module google.golang.org/grpc to v1.81.1 ([#55](#55)) ([6bfac2d](6bfac2d)) * status lifecycle improvements and created/running result handling ([#5](#5)) ([fc64c7d](fc64c7d)) * **status0:** white page header and de-duplicated footer ([#48](#48)) ([77506a7](77506a7)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Sep 8, 2026
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Status pages
Dashboard conventions
Specs (`specs/todos/`)
Five spec drafts covering alerting/incident design ideas — each independently shippable:
Docs (housekeeping)
`docs/CLAUDE.md` size rule (500-line soft / 800-line hard cap) added and applied; long competitor docs reorganized into per-topic subdirectories. `docs/research/alerting-patterns.md` consolidates the synthesis used to seed the specs above.
Test plan
🤖 Generated with Claude Code