Repository navigation
batch/2026-08-05: status-page availability thresholds, slug max length 100, ?q= URL sync, agents fleet view - #196
Merged
Merged
Conversation
Add fly.nrt.toml for `solidping-agent-nrt`, the platform check agent staffing the new `jp-1` (Tokyo) region against the k8xp dev API. Identity is pinned via the SP_AGENT_KEYS secret rather than a volume: loadIdentity() prefers SP_AGENT_KEYS over the keys file, and because the pinned identity is already enrolled, every boot takes the reconnect path. That keeps the agent row stable across `fly deploy` (which recreates machines) and avoids persistIdentity(), which logs the private keys in the clear and is only reachable from the enrollment path. The trade-off — fly secrets are app-wide, so this must return to the volume model before scaling past one machine — is documented in the toml. Sized at 256mb from measured usage: the equivalent in-cluster workers sit at 27 MiB and 93 MiB. Also fix the README's SP_AGENT_NAME guidance, which recommended a `/bin/sh -c` ENTRYPOINT that cannot run against the distroless image, and would have given every machine in a region the same name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ket calibration guard - settings jsonb/text column with typed StatusPageSettings/AvailabilitySettings - PATCH semantics: no deep merge, unknown keys rejected, explicit null resets a section to defaults, absent settings leaves the page untouched - availabilityToStatus parameterized on the page's effective thresholds and a single-failure calibration guard (exactly one failed sample never renders down) - public + admin payloads expose the resolved effective thresholds
… defaults Badges stay check-scoped on the global thresholds (spec 2026-08-03-01 Decisions) — this only removes the literal duplication, no behavior change.
Extract validateStatusPageUpdate/createRequestAvailability to keep Create/UpdateStatusPage under the complexity budget; drop named returns and one-letter identifiers flagged by golangci-lint.
…ion guard, PATCH semantics, public payload Table-driven, testify/require, t.Parallel(): availabilityToStatus with custom thresholds and defaults-unchanged positive control; the 1-vs-2 failure calibration guard; validateAvailabilitySettings and parseSettingsField; end-to-end PATCH semantics (absent, replace, explicit-null reset, invalid pair rejected); create-time settings; and the public ViewStatusPage payload exposing resolved effective thresholds.
Two numeric inputs (up/degraded, empty = default) next to Show Availability, edit form only. Also documents settings/availabilityThresholds on the StatusPage OpenAPI schema and the settings PATCH semantics.
… form Authored against the shared status-pages.spec.ts pattern (create → edit → set/save/reload → clear/save/reload). Could not be run locally: the :4000 devloop in this worktree is not in SP_RUNMODE=test and must not be disturbed (a separate session is using it).
…holds Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Backend regexes for checks, check groups, status pages, and severities now accept 3-100 char slugs (was 3-20/3-40, inconsistent). Updates error messages and the stale checks/service.go comment to match. Org slugs, private region slugs, and auto-generation caps are deliberately left unchanged.
Mirrors backend regex widening: slugify() truncation, check-form and status-page-form patterns/messages, and the checks-index/check-groups-edit group slug regexes now all allow 3-100 chars, with comments updated to match.
…n 009 The spec's premise that "the limit is purely regex-side" was wrong: check_groups/checks/status_pages/status_page_sections carry DB-level length CHECK constraints (40/50 chars, mirroring the old Go regex) in both the sqlite and postgres migration 001. Without widening these too, a slug between 41/51 and 100 chars would pass the widened Go regex and then be rejected by the database as a 500, worse than before. Postgres: simple drop/add constraint swap. SQLite has no ALTER COLUMN/DROP CONSTRAINT, so each table is rebuilt with the *_new create/copy/drop/rename pattern already established by 005_v0_4_0.up.sql, including disabling foreign_keys for the duration (these tables are FK-referenced parents; with enforcement on, DROP TABLE fires child ON DELETE actions against live rows before the rebuilt table is swapped back in) and spelling out explicit column lists in INSERT ... SELECT instead of `select *`, which is positional and would silently scramble data on any column-order drift. Verified via a dedicated test that seeds rows in all four tables under migrations 001-008 only, then runs 009 and asserts every field (including the checks table's late-added flap_*/config_sealed/ region_spread columns) and FK relationships survive unchanged. Also corrects the spec's "no migration needed" text and adds a Proposal item documenting the required migration.
Adds 100-accepted/101-rejected/3-accepted/2-rejected cases to the checks/checkgroups/severities/statuspages slug validation tests, plus an end-to-end test per resource (through the real SQLite migration chain, not just the in-memory regex) proving a 100-char slug actually persists now that the DB-level CHECK constraints are widened too. Also fixes stale slug-length doc comments left over from the earlier 20/39/49-char regexes.
The spec asserted slug length limits were purely regex-side (no varchar(N) in migrations) and explicitly said not to add a migration. That was wrong: check_groups/checks/status_pages/status_page_sections carry DB-level length CHECK constraints mirroring the old regex bounds. Documents the correction and adds Proposal item 5 covering the required migration, landed in the previous two commits.
lll, revive context-as-argument, sqlclosecheck and testifylint findings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
make build-backend + lint-back (0 issues) + test all green; make build-dash0 green and dash0 eslint at 44 errors, unchanged from base with none introduced in the touched files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seeds local search state from the URL on mount (lazy initializer, same cold-load fix as discovery.new.tsx) and writes the debounced value back with replace:true so the query is bookmarkable and survives reload/back-navigation.
status-updates, status-pages, maintenance-windows, integrations, escalation-policies, and dependencies index pages now mirror their client-side search/filter box in ?q=, seeded from the URL on mount (cold-load safe) and written back debounced with replace:true. dependencies.index.tsx's local filter state is renamed search/setSearch to match the common pattern; the q URL param and existing data-testid="dependencies-filter" are unchanged.
Adds a data-testid to the checks search input and a Playwright E2E (checks-search-url-param.spec.ts) covering typed search -> URL update -> reload persistence, cold deep-link ?q=, and q surviving groupBy changes and vice versa. Authored but not run locally: the :4000 devloop here is not SP_RUNMODE=test (test-org login 401s).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
New DB method returning every non-deleted agent (org + system kind) across all organizations, mirrored in sqlite and postgres per the sync-pg-to-sqlite discipline. Backs the upcoming superadmin fleet view — today system agents (no owning org) are visible nowhere short of a direct DB query.
Superadmin-only endpoint listing every agent across all orgs plus platform-operated system agents. AgentResponse gains optional kind and org fields (org-scoped ListAgents leaves them omitted since every row there is implicitly kind=org).
Service-level test asserts both an org agent (with its org slug attached) and a system agent (no owning org) are returned by ListAllAgents. Handler-level test exercises the RequireSuperAdmin gate on the new route: non-superadmin gets 403, superadmin gets 200. Also implements the new mockDBService.ListAllAgents method required by the db.Service interface change.
New superadmin-only tab listing every deployed agent — org-enrolled and platform-operated system agents alike — with Kind/Org/Name/Region/ Fingerprint/Last seen/Status columns. Modeled on the existing AgentsCard, using regionDisplayLabel for regions and a 30s refetch like useAgents. Highlights an active agent whose lastSeenAt is older than 5 minutes with a "Stale" warning badge: connected agents refresh last_seen_at on every ping tick, but the GC job only retires a silent system agent after 7 days, so without this cue a dead-for-days agent looked fine.
tabs.agents plus the full agents.* section (title, description, empty state, column headers, stale badge) in en/fr/de/es.
The "system agents are listed nowhere / a mgmt read view is deliberately out of scope" note is stale now that GET /api/v1/system/agents and the dash0 server-page Agents tab ship. Also documents the new route in the agents API spec page.
Coordinator-verified: make build-backend, lint-back (0 issues) and test all green; make build-dash0 green and dash0 eslint at 44 errors, unchanged from base. The one added warning (react-refresh/only-export-components) matches the existing sibling server.*.tsx route files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The `?q=` URL sync added in 2026-08-04-02 fired its write-back effect unconditionally on mount, including when the URL already matched the (empty) search box. `navigate` is anchored to the list route via `from`, so that redundant call re-targets the route from wherever the router has since moved. On a logged-out deep link that is fatal: the /orgs/$org guard has already bounced to /login, the still-mounted list page navigates back to itself, and the guard bounces again — this time folding login's own search params into returnTo. Each pass nests returnTo one level deeper, so the URL grows without bound until the renderer hangs with no document at all. Any logged-out deep link to checks, dependencies, escalation-policies, integrations, maintenance-windows, status-pages or status-updates hit it, which is what took out the login deep-link E2E test. Only write when the URL is actually stale. Verified as a positive control: the three new logged-out deep-link tests fail (hung renderer, 40s timeout) against a build without the guard and pass with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Clicking Save fires a PATCH from the SPA with no document navigation, so
the `waitForLoadState("networkidle")` that followed it could resolve
before the request was even issued. The `page.reload()` right after then
raced the write and re-rendered the previous server state — the test read
"Notification connection" where it expected "All admins", and "All admins"
where it expected "User". This was failing all three attempts on main.
Wait for the PATCH response itself. Ran green 3x in a row locally.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ssertion live-updates.spec.ts:350 flaked in CI: the row stayed visible for the whole 15s window. The single UI budget had to cover server-side incident resolution AND the 3s LIVE_INVALIDATE_MIN_INTERVAL_MS damper AND the refetch, so a slow server under CI load consumed the budget meant for the live path. Poll the incidents API until the incident is actually inactive first, then assert the UI hid the row. The UI assertion now measures only what the test is about — that the live path delivers a server-side change without a reload. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fclairamb
added a commit
that referenced
this pull request
Aug 5, 2026
Correct the release cut. Squash-merging #196 collapsed its seven feat: commits into a single non-conventional subject, so release-please saw no feature and proposed a 0.7.2 patch with a changelog listing only the DNS fix — for a release that ships four features and a migration named 009_v0_8_0. Set the version to 0.8.0 and write the changelog from the squashed commits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Batch of four specs from
specs/todos/, each implemented by an isolated subagent and gated on an independent completeness audit before archiving. Also carries the fly.io Tokyo agent deployment config.Specs
2026-08-03-01— Per-status-page availability color thresholds (sonnet/high)Per-page green/amber/red thresholds via a new typed
settingsjsonb column onstatus_pages, plus a small-bucket calibration guard so a single failed sample can never render a bucket red (fixes the "one failed minute = red hour" cliff on 1-minute checks). PATCH uses whole-section replace (no deep merge) withDisallowUnknownFields; the public payload always exposes resolved effective thresholds. Badges deliberately stay on the global defaults.2026-08-04-01— Entity slug max length 100 (sonnet/low)Slug cap raised to 100 across checks, check groups, status pages and severities, backend and frontend.
The spec's premise was wrong and this PR deviates from it deliberately. It claimed slug limits were "purely regex-side" and instructed not to add a migration. There are in fact DB-level CHECK constraints in both dialects (
001_v0_1_0.up.sql: sqlitelength(slug) <= 40/50, postgres{2,39}/{2,49}). Shipping it as written would have let a 90-char slug pass validation and then fail at the database — a 500 instead of a validation error. Migration009_v0_8_0therefore widens the constraints too, and the spec text was corrected in-tree.SQLite can't alter constraints, so the four affected tables are rebuilt with the
*_new+PRAGMA foreign_keys=OFFpattern already established in005_v0_4_0. INSERTs name every column explicitly (notselect *) to remove the positional-drift hazard, andmigration_009_slug_rebuild_test.goseeds every column with distinguishable non-default values before 009 and asserts them byte-for-byte after, plus FK survival andPRAGMA foreign_key_check.2026-08-04-02— Search inputs sync to the URL as?q=(sonnet/medium)All seven dash0 list pages mirror their search box to
?q=. Local state is seeded from the URL via a lazy initializer and written back debounced withreplace: trueand a functional updater —validateSearchalone is not enough, since URL-only state is dropped on a cold deep-link under a layout route. E2E covers type→URL, reload, and cold deep-link.2026-08-05-01— Fleet-wide Agents tab on the server page (sonnet/medium)GET /api/v1/system/agents(superadmin) plus a server-page tab listing every agent across the instance, includingkind='system'agents that belong to no org and were previously listed nowhere. Includes a >5min staleness cue, because the GC only retires system agents after 7 days — so a dead-for-days agent otherwise looks healthy.Also included
chore(fly):deploy/fly/fly.nrt.tomlforsolidping-agent-nrt, the Tokyo system agent staffing the newjp-1region, with identity pinned viaSP_AGENT_KEYSrather than a volume. Also fixes the README'sSP_AGENT_NAMEguidance, which recommended a/bin/shENTRYPOINT that cannot run against the distroless image.QA
Each spec ran its scoped gate; the coordinator re-ran
make build-backend,lint-back,test,build-dash0and dash0eslintindependently rather than trusting the reports. Backend lint 0 issues, tests green, dash0 eslint held at its 44-error pre-existing baseline with none introduced.Not included
specs/todos/2026-08-04-03-agent-never-logs-private-keys.mdwas excluded — a concurrent session owns it.🤖 Generated with Claude Code