11 Commits

Author SHA1 Message Date
Jordan Ritter c68a0e519c Add e2e-smoke probe config
30-minute schedule (half the smoke cadence — L3+L4 per tick pulls a
real chromium page so we trade freshness for memory + bandwidth
headroom). 180s outer timeout covers two 60s page timeouts plus
chromium launch overhead. max_concurrency 2 caps peak memory at ~600MB
(each chromium ~300MB RSS) to fit the orchestrator's Railway footprint.
Discovery uses the railway-services source with namePrefix + the new
nameExcludes filter to hit every user-facing showcase without the
7 infra services.
2026-04-22 13:44:38 -07:00
Jordan Ritter e90c4a385c Bundle registry.json + install Playwright chromium in ops image
Runtime stage needs two things for e2e-smoke: chromium (headless) and
the shell registry so the driver can gate L4 on demos membership.
Build stage pulls the single registry.json file through rather than
fetching at probe-tick time, keeping the image hermetic. Runtime
stage is already on Debian-slim (needed by Playwright's glibc-linked
chromium); `playwright install --with-deps chromium` runs as root
before the USER node drop so apt-get succeeds. /ms-playwright is
chown'd to node so the runtime user can read the browser tree.
2026-04-22 13:44:38 -07:00
Jordan Ritter 2ca7ea1be3 Register e2e-smoke driver with the probe registry
Wire the in-process Playwright driver so probe ticks resolving to
kind: e2e_smoke dispatch to it. Drops the 'deliberately NOT
registered yet' placeholder that flagged the driver as scaffold-only.
2026-04-22 13:44:38 -07:00
Jordan Ritter 0f0e95334c Add nameExcludes filter to railway-services discovery
The e2e-smoke probe needs to discover every user-facing showcase
service without enumerating infra (aimock, ops, pocketbase, shell,
shell-{dashboard,docs,dojo}). Extend the discovery filter schema
with an optional nameExcludes array so a single namePrefix can pull
in all 34 services and explicitly drop the 7 that don't expose user
demos, rather than maintaining 27 separate namePrefix entries.
2026-04-22 13:44:32 -07:00
Jordan Ritter d29e39b1cb Add Playwright in-process e2e-smoke driver for L3 + L4 coverage
Replace the JSON-reporter scaffold with an in-process Playwright
driver. Each invocation launches one headless chromium, runs L3
against /demos/agentic-chat (any non-empty response) and, when the
service registry entry includes tool-rendering, runs L4 against
/demos/tool-rendering (weather vocabulary check). Primary return is
e2e-smoke:<slug>; chat:<slug> and tools:<slug> side-emit via
ctx.writer. AbortSignal is wired end-to-end so timeout teardown
kills the browser cleanly. Launcher + demos resolver are dep-injected
for unit testability.

Input schema accepts { key, backendUrl?, publicUrl?, name?, demos? }
with a zod refine requiring at least one URL. Slug derivation strips
the "showcase-" prefix so Railway service names align with registry
entries (bare slugs). The default demos resolver memoises a read of
/app/data/registry.json at first use, keeping probe-tick overhead at
a single JSON parse across the orchestrator lifetime.
2026-04-22 13:44:19 -07:00
Jordan Ritter 7b8bbc64d5 feat(showcase-ops): smoke probe L2 agent check + railway-services auto-discovery
Extends the smoke probe driver to emit a third side-emission per target:
`agent:<slug>` — POST `${backendUrl}/api/copilotkit/` with `{}` body,
green on non-404 response (runtime is mounted), red on 404 or transport
failure. Matches the `checkAgentEndpoint` contract in the e2e helpers
(integration-smoke.spec.ts L2 `@agent` assertion).

Switches `config/probes/smoke.yml` from a static 17-target list to the
`railway-services` discovery source with `namePrefix: "showcase-"` and
an explicit `nameExcludes` for infra services (aimock/ops/pocketbase/
shell*). New showcase services are picked up automatically on the next
tick without YAML edits.

- src/probes/discovery/railway-services.ts: add `nameExcludes: string[]`
  filter field, applied AFTER the prefix check so excluded services skip
  per-service env fetch entirely.
- src/probes/drivers/smoke.ts: accept both static (`{key,url}`) and
  discovery (`{key,name,publicUrl,imageRef,env}`) input shapes. Derive
  slug by stripping `showcase-` prefix from `name` (discovery) or
  splitting key on `:` (static fallback). Rewrite primary result key to
  `smoke:<slug>` in discovery mode so existing alerts keyed on
  `smoke:ag2` / `smoke:starter-ag2` stay intact.
- Sequential probes kept for socket-count bound: at max_concurrency=6 *
  34 services * 3 endpoints, parallel would push 612 inflight sockets.
- Coverage: smoke driver 98.55% lines, exceeds 95% gate.
2026-04-22 13:44:19 -07:00
Jordan Ritter cbb4d5d382 feat(showcase/ops): L1-L4 alert rules for agent/chat/tools depth signals
Adds three new alert-rule YAMLs modeled on smoke-red-tick.yml so the
per-starter depth signals side-emitted by the smoke + e2e-smoke drivers
(agent:<slug>, chat:<slug>, tools:<slug>) produce Slack alerts on
green_to_red / sustained_red / red_to_green transitions with the same
!channel 1-hour escalation + 15-minute rate limit + 20-minute deploy
grace as the L1 smoke rule.

Also extends the DIMENSIONS enum with the three new closed values so
rule-side typos continue to fail at load with a listed-valid-members
Zod error instead of silently never matching any probe key.

No driver or discovery changes in this commit — pure rule YAML + the
single enum entry each rule's `signal.dimension` resolves against.
2026-04-22 13:44:19 -07:00
Jordan Ritter 0c1baa8ecf feat(showcase/ops): Dockerfile + README + rotation drill
Multi-stage Dockerfile built on bookworm-slim with Playwright chromium
pre-installed (e2e-smoke runner) + pnpm-workspace.yaml + every
workspace package.json COPYd so the runtime stage resolves workspace:*
deps. README organised operate-first / build-second so an on-call
operator can configure + inspect the service before needing to rebuild
it. Rotation drill doc captures the weekly / monthly schedule for
pin-drift + redirect-decommission audits.
2026-04-22 11:00:47 -07:00
Jordan Ritter c69aeca835 feat(showcase/ops): probe driver architecture + 6 drivers + 2 discovery sources + YAML configs
Split the probe system into reusable contracts so every future probe
can share one loader + invoker + discovery enumeration path:
- ProbeDriver<Input, Signal> + DiscoverySource<Output> contracts
  (src/probes/types.ts)
- ProbeConfigSchema Zod union (static targets / discovery-driven /
  single-target) + chokidar-watched probe-loader + probe-invoker with
  AbortController-based per-target timeout + bounded concurrency
  (src/probes/loader/*)
- DiscoverySource typed error classes (Auth / Backend / Transport /
  Schema) + railway-services + pnpm-packages discovery sources
  (src/probes/discovery/*)
- 6 probe drivers: smoke (paired health side-emit), aimock-wiring,
  image-drift, pin-drift, version-drift, redirect-decommission.
  e2e-smoke driver scaffolded but unregistered pending runner wiring.
  redirect-decommission ships an inlined hermetic copy of the CLI
  formatter + seo-redirects catalogue to avoid runtime dynamic imports
  (src/probes/drivers/*)
- 6 probe YAML configs: smoke.yml, aimock-wiring.yml, image-drift.yml,
  pin-drift.yml, version-drift.yml, redirect-decommission.yml.
  Orchestrator registers these at boot; YAML edits hot-reload via
  chokidar + reloads/failures emit on the event bus.

Tests: 95% line-coverage floor on drivers + discovery, fixtures for
every driver's happy + error paths.
2026-04-22 11:00:47 -07:00
Jordan Ritter 8c0e2e07c2 feat(showcase/ops): alert rule DSL + engine + YAML configs
Alert rule DSL with Mustache templates, trigger-flags + transition
matching, closed-enum registries for kind/dimension/signal-filter/
action, triple-brace safe-field allow-list enforcement, escalation +
suppress guards. Ships 8 alert YAML rules:
- _defaults.yml (shared template + severity)
- smoke-red-tick, aimock-wiring-drift, image-drift, deploy-result,
  pin-drift-weekly, version-drift-weekly,
  redirect-decommission-monthly, e2e-smoke-failure

Each rule maps 1:1 onto a retired GH Actions cron workflow + keeps the
existing Slack message shape so #oss-alerts consumers are unchanged.
2026-04-22 11:00:47 -07:00
Jordan Ritter 9d04bdffb0 feat(showcase/ops): core service (HTTP + HMAC + metrics + orchestrator + rules + render + storage + scheduler + targets)
In-cluster Railway service replacing the legacy cron-based GH Actions
workflows. Core pieces:
- Hono HTTP server with HMAC-signed webhook ingress + Prometheus
  metrics + deploy webhook handler (src/http/*)
- structured logger with log-level filtering (src/logger.ts)
- event bus + transition detector (src/events/*)
- orchestrator wiring rule + probe + target registries and the
  scheduler loop (src/orchestrator.ts)
- rule loader + Zod schema for alert YAML (src/rules/*)
- Mustache renderer + regex filter helpers with triple-brace
  safe-field allow-list (src/render/*)
- scheduler loop + bounded concurrency (src/scheduler/*)
- PocketBase client + alert-state store + S3 backup (src/storage/*)
- Slack webhook target (src/targets/*)
- shared types (src/types/index.ts)
- legacy probes (aimock-wiring, smoke, pin-drift, redirect-decommission,
  deploy-result) kept as authoritative signal shapes; drivers wrap these
Plus load-test + notify-ops-jq scripts + valid/invalid rule test
fixtures.
2026-04-22 11:00:47 -07:00