Repository navigation
feat: config-as-code CLI, SP_NODE_NAME worker identity, docs showcase media, and agent key redaction - #198
Merged
Conversation
… entry into the logs
…he shared worker session sessions.spec.ts and the two mcp-oauth-consent.spec.ts tests that depend on their refresh-token row surviving (session list isCurrent, /auth/refresh bounce) no longer ride the shared authWorkerStorageState session, which enforceSessionCap (server/internal/handlers/auth/service.go, cap=10) evicts as the least-recently-active row once ~60+ unrelated fresh logins land elsewhere in the suite. Also makes sessions.spec.ts:80 target the session it created by uid instead of "the first non-current row".
Two consecutive full E2E runs (--retries=0, single worker, fresh SP_DB_RESET side-car server) both pass sessions.spec.ts and mcp-oauth-consent.spec.ts on the first attempt. Only remaining failure is the pre-existing, unrelated kubernetes-clusters.spec.ts:36 (hardcoded localhost:4000, out of scope). bun run lint: no new errors in touched files (fixtures.ts, sessions.spec.ts, mcp-oauth-consent.spec.ts) against the pre-existing dash0 eslint debt.
Adds the missing ### Verification (Proposal 4) section: per-run test counts for the baseline and two post-fix full-suite runs, explicit sessions.spec.ts:4 and mcp-oauth-consent.spec.ts:156 status per run, and honest reconciliation of the baseline's other two failures (kubernetes-clusters.spec.ts:36 is a pre-existing wrong-base-URL bug, independently verified in all three runs; discovery.spec.ts:357 did not recur post-fix and is flagged as an unproven one-off). Also records that mcp-oauth-consent.spec.ts:156 was never reproduced locally (it passed pre-fix too), so that half of Proposal 4's acceptance criterion is CI-confirmable only, with the login-count reasoning for why the theory still plausibly holds. Corrects the plan's endpoint reference (GET /api/v1/orgs/test/tokens?type=refresh, not the nonexistent GET /api/v1/auth/sessions) to match the shipped code.
Exports ParseManifest (was parseManifest) and adds ValidateDocument, a pure/offline validator for the generic export-document format rules (version, slug uniqueness/format, per-type config via each checker's own Validate, duration/label/region formats, no inlined credentials, expectedStatusCodes vs expectedStatus exclusivity, dependency graph soundness). Org-specific convention rules are intentionally out of scope. Groundwork for sp checks validate <file> (offline document validation).
Auto-detects a top-level array and routes to the new ValidateDocument path (no token, no network) instead of the existing single-check online validate. Satisfies Proposal 4's sp checks validate <file> without renaming or shadowing the pre-existing single-check command.
- sp checks export gains --format yaml|json, defaulting from the -o
extension. JSON->YAML transcoding decodes via json.Decoder.Token()
straight into a yaml.Node tree (never through map[string]interface{})
so exports stay key-order stable.
- sp checks import now sets Content-Type from the file extension
(reusing detectContentType) and prints a created/updated/skipped
summary with per-item [index] slug: error lines, exiting non-zero on
any failure. client.SolidPingClient.ImportChecks gains a contentType
parameter.
- New sp checks diff <file>: strips exportedAt from both sides,
normalizes to sorted-key JSON, and prints a go-difflib unified diff.
Exit 0/1/>=2 (no drift / drift / errors).
Covered by checks_yaml_test.go (order preservation, determinism,
round-trip stability, format inference), checks_diff_test.go
(exportedAt stripping, drift detection, JSON/YAML equivalence), and
apply_import_test.go (summary/exit-code contract).
Adds a Config as Code section to web/docs/docs/cli.md: the export -> edit -> validate -> import --dry-run -> import -> re-export loop, the import-is-an-upsert-never-deletes caveat (with sp apply --prune as the delete-by-absence alternative), and the export --format behavior.
make build-backend lint-back test and make build-docs all green. Renamed a test-fixture label key (env -> environment) to clear a goconst threshold that the new test data pushed a pre-existing occurrence count over.
Gap 1 (real bug): checker.Validate mutates spec.Config in place for heartbeat/email checks (auto-generates a "token" when absent) - correct for the live create/update path, but ValidateDocument was passing the check's own Config map by reference into that call, so its own no-inlined-credentials heuristic then flagged the token it had just caused to be generated. The reference workflow's real config.yaml failed validation as a result. Fix: deep-copy each check's Config (JSON round trip) before handing it to checker.Validate; credential/ status-field checks still run on the original, untouched map. Added TestValidateDocumentDoesNotMutateInput (heartbeat + email) as a regression guard, and confirmed `sp checks validate ~/code/stonal/exp-devops/solidping/config.yaml` now exits 0. Gap 2: factored checksDiffAction's drift/exit-code decision into pure computeDiffOutcome + diffExitCode (mirroring formatImportSummary for import), and added table tests for no-drift (0), drift (1), and error (>=2) - the spec's "diff exit codes with and without drift" test area was previously only exercised indirectly via normalizeForDiff. Gap 3: TestJSONToYAMLRoundTripStable was calling jsonToYAML twice on the same JSON string (a duplicate of the determinism test, not a round trip). Replaced with a real round trip: parse the produced YAML back with yaml.Unmarshal into a yaml.Node and re-render with the same encoder settings, asserting byte-identical output. Key-order-matches- wire-order is now its own small test. Gap 4: added TestIsExportDocumentShape, table-testing the predicate that routes `sp checks validate` between the offline whole-document path and the existing online single-check path - including the adversarial case of a single check whose own config has a nested "checks" key.
make build-backend lint-back test green after closing the four audit gaps (ValidateDocument purity bug, diff exit-code test coverage, genuine YAML round-trip test, isExportDocumentShape routing test). sp checks validate against the real reference config.yaml now exits 0.
… hardcode localhost:4000) silently talk to the wrong server
kubernetes-clusters.spec.ts and discovery-scan-method.spec.ts derived their API base from E2E_API_BASE, an env var nothing sets, so they always hit localhost:4000 regardless of E2E_BASE_URL - silently targeting the wrong server/database when a side-car test server runs on another port. Delete the dead E2E_API_BASE fallback entirely and import the correct API_BASE from fixtures.ts, like the rest of the suite. Also consolidate every other file that re-derived the same origin (webpush, discovery, global-setup, and the "page-URL variant" files which turn out to compute the same API origin, not a distinct page URL) onto the single fixtures.ts export, plus two files found by a broader grep sweep (login.spec.ts, account-notifications-whatsapp) that had the same literal localhost:4000 fallback pattern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… only described in text, never shown
…oduct is only described in text, never shown
…t is only described in text, never shown
…t is only described in text, never shown
…lose persistent org
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t is only described in text, never shown
…t is only described in text, never shown
… is only described in text, never shown
Worker identity was derived entirely from os.Hostname(), lowercased and cut
to 15 characters. On Kubernetes with hostNetwork: true the pod shares the
host UTS namespace, so os.Hostname() returns the (dotted) node name and the
workers.slug CHECK constraint rejects it — the worker never registers.
Truncation also silently collapses two workers onto one upsert-by-slug row.
SP_NODE_NAME now sets slug and name verbatim, skipping the hostname read
entirely. The effective slug is validated at startup against the same
^[a-z][a-z0-9-]{2,20}$ pattern the database enforces, and a truncated
hostname logs a WARN naming the resulting slug. Both the check worker and
the job worker share config.Config.WorkerIdentity().
…os.Hostname(), which blocks IPv6 egress on Kubernetes
Covers the end of the chain the config tests reach from the other side: the check worker's workers row (slug + name) and the job worker's self-stats slug both come from the shared config.Config.WorkerIdentity resolution.
…os.Hostname(), which blocks IPv6 egress on Kubernetes
…s.Hostname(), which blocks IPv6 egress on Kubernetes
…DE_NAME, showcase media and agent key redaction
fclairamb
enabled auto-merge (squash)
August 7, 2026 21:00
Merged
fclairamb
added a commit
that referenced
this pull request
Aug 7, 2026
🤖 I have created a release *beep* *boop* --- ## [0.9.0](v0.8.0...v0.9.0) (2026-08-07) ### Features * config-as-code CLI, SP_NODE_NAME worker identity, docs showcase media, and agent key redaction ([#198](#198)) ([ec971d6](ec971d6)) --- 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
Six specs implemented over 2026-08-05 → 2026-08-07. Each was implemented, QA'd against its scoped targets, and cleared by an independent completeness audit before being archived to
specs/done/2026/08/.Security
SP_AGENT_PRINT_KEYS. A malformedSP_SYSTEM_AGENT_ENROLLMENT_TOKENSentry is no longer echoed either.Features
sp checks export/importround-trip a check catalog as YAML,sp checks diffshows what an import would change before it runs, andsp checks validateaccepts a whole export document offline — no server, no credentials — so a catalog can be linted in CI.SP_NODE_NAMEworker-identity override. Identity was hard-wired toos.Hostname(), which broke registration under KuberneteshostNetwork: true(shared UTS namespace → node name with dots →workers_slug_checkviolation) and silently collapsed two workers onto one row whenever their hostnames shared 15 characters. Both workers now share one helper, the effective slug is validated at startup against a regex guard-tested to match the DB CHECK constraint, and hostname truncation emits a warning naming the resulting slug.make showcase) drives the real dashboard to produce screenshots and an AV1 recording of the create-an-HTTP-check flow. Regenerates on demand rather than rotting as the UI changes; no CI wiring, by design.Fixes
E2E_API_BASEvar or hardcodedlocalhost:4000, so a suite pointed at a test server was quietly validating the dev loop instead. Both now derive their origin from a shared fixtures module, with a lint guard in each config to stop it regressing.kubernetes-clusterspec cleans up on assertion failure.ValidateDocumentno longer mutates its input.No breaking changes. One behaviour change worth noting: a worker whose effective slug is illegal now fails fast at startup with an actionable message, where it previously started and then failed to register with an opaque
SQLSTATE=23514.Test plan
make build-backend lint-back testgreen.make build-dash0+bun run lint— no new errors in touched files (the ~44 pre-existingreact-hookserrors are unchanged baseline).make build-status0+bun run lintclean; the new eslint guard verified to actually fire on all three banned patterns, and to exempt the fixtures module.make build-docsgreen, all four/docs/showcase/*assets resolving in the built page.ffprobe, stills visually verified.SP_NODE_NAMEbinding proven end-to-end throughLoad(), and the validator regex guard-tested against the actual migration constraint.Not covered
One acceptance criterion on the worker-identity spec is deliberately unchecked: running a check worker with
hostNetwork: trueagainst an IPv6-only target needs a released build deployed from the k8xp repo.SP_NODE_NAMEis what unblocks it.Four of the five status0 e2e specs still can't run against a test-mode side-car — they navigate to the
defaultorg, which only themake devseed creates. Pre-existing run-mode coupling, not introduced here.🤖 Generated with Claude Code