feat(showcase): run HTTP-only probe families in-process on the fleet control-plane#5284
Merged
Conversation
jpr5
requested review from
marthakelly,
mme,
ranst91 and
tylerslaton
as code owners
June 5, 2026 21:46
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
Contributor
📣 Social Copy GeneratorGenerate social media copies (Twitter/X, LinkedIn, Blog Post) for this PR using Claude.
|
Contributor
Production Digest-Pinning AuditAudit could not run this round. ErrorRun 2026-06-05 15:49:10 PDT — 0 finding(s). |
5 tasks
jpr5
added a commit
that referenced
this pull request
Jun 5, 2026
…ucer schedules (#5285) ## Summary Producer-side foundation for fleet framework item 3b. Two **behavior-preserving** generalizations that make the seam capable of multiple browser families and multiple producer cadences, while keeping the d6 case **byte-identical**. No wiring is flipped on yet (see Out of Scope). ### 1. Parameterized service enumerator `showcase/harness/src/fleet/control-plane/catalog-enumerator.ts` - New generic `createServiceEnumerator(params)` (catalog-enumerator.ts:215) takes the service-set `filter`, the `driverKind`, and a `probeKeyPrefix` (string prefix → `<prefix>:<slug>`, or a builder fn). - `createD6ServiceEnumerator` (catalog-enumerator.ts:280) is re-expressed as a thin call passing the d6 params: `D6_DRIVER_KIND` (`e2e_d6`), prefix `"d6"` (→ `d6:<slug>`), and `D6_DISCOVERY_FILTER`. d6 output is unchanged — same services, same filter, same kind, same keys. ### 2. Control-plane accepts an array of producer schedules `showcase/harness/src/fleet/control-plane/control-plane.ts` - New `ProducerSchedule` type (`{ scheduleId, cron, producer }`) + a `schedules?` dep on `ControlPlaneDeps`. - `createControlPlane` normalizes to an array (control-plane.ts:~232); omitting `schedules` degenerates to the single d6 schedule on `FLEET_PRODUCER_SCHEDULE_ID` (`fleet-job-producer`) @ `40 * * * *` — current behavior preserved exactly. - `start()` / `stop()` iterate the array, registering/unregistering each scheduler entry and starting/stopping each producer. ## Out of scope (deferred — gated on other in-flight PRs) - **No `runControlPlane` wiring** to actually PASS multiple schedules — that edit conflicts with in-flight **#5284** (which edits `runControlPlane`) and is deferred. This PR only makes `control-plane.ts` *capable* of N schedules + generalizes the enumerator seam; the wiring lands later. - No `e2e_smoke` / `e2e_demos` / `e2e_deep` enumerators or producers (Phase 2). - No changes to `worker-loop.ts` / `payload-mapper.ts` / `probe-loader.ts` (other PRs own those). - No driverKind constant / contract changes. ## Test plan - [x] Red→green TDD: 3 new enumerator tests (generic kind/keys/filter/fn-prefix) + 2 new control-plane tests (N entries registered with distinct crons; stop tears all down) failed before impl, pass after. - [x] Equivalence: all pre-existing d6-enumerator + single-schedule control-plane tests pass unchanged. - [x] Full harness suite green: **2078 passed** (119 files). - [x] `tsc -p tsconfig.build.json` clean (exit 0). - [x] Only the 4 intended files changed; no lockfile drift. Do not merge — producer-side foundation only; wiring follows after #5284 lands.
…control-plane
The fleet control-plane previously ran only the d6 producer, leaving the 8
HTTP-only probe families (smoke, starter_smoke, image_drift, qa, aimock_wiring,
version_drift, pin_drift, redirect_decommission) dark on the fleet. Lift the
legacy boot() probe-loader machinery into runControlPlane so those families run
in-process:
- Add BROWSER_KINDS = {e2e_d6, e2e_smoke, e2e_demos, e2e_deep}; HTTP = every
other kind. Add registerHttpProbeDrivers (HTTP-only driver set, no BrowserPool
drivers).
- In runControlPlane, build an HTTP-only probeRegistry + discovery registry
(railway-services cached + pnpm-packages, mirroring boot()), a probe-loader
scoped to HTTP kinds, and the same diffProbeSchedules/buildProbeInvoker loop
boot() uses — registering one probe:<id> scheduler entry per YAML config.
Crons are driven from the YAML schedule. Browser e2e_* YAMLs route to the
worker producer path and are NOT scheduled in-process.
- Add an includeKind predicate to createProbeLoader so a browser YAML on disk is
SKIPPED (not rejected) against the HTTP-only registry — no spurious
probes.reload.failed.
- /health: ruleCount now reflects the in-process HTTP probe count (was a
hardcoded 0). The control-plane role still drops the rules>0 gate, but the
real count means a silent probe-loader failure is visible on /health rather
than masked. schedulerJobCount already counts the new probe entries.
- Tear down the HTTP-probe file watcher on stop() and on bind failure.
…d CR gap-fills Address the CR gaps on the in-process HTTP-probe control-plane: - Sweep orphaned `running` probe_runs at control-plane boot (boot()'s sweepStaleRuns never ran in fleet mode → leaked rows forever). Best-effort; a sweep failure does not abort boot. - Add a fail-loud BROWSER_KINDS / HTTP-driver disjointness assert at boot plus a drift-lock test mirroring registerAllProbeDrivers, so a mis-added kind can't silently go dark. - Assert the PR's headline guarantees that were unasserted: cron-from-YAML (exact values), /health loader-failure boot-survival + probes.reload.failed emit + rules=0 observability, hot-reload add/remove + watcher teardown, a discovery-backed family (qa) in the in-process schedule, and tightened /health rule/job counts to exact equality. - Bump the includeKind skip log to info; make diffHttpProbeSchedules' unregister-failure post-state explicit (keep config observable); correct the /health "no longer masks" comment, the discovery-source comment (image_drift uses railway-services; version_drift uses pnpm-packages), the reload-failed surface comment (no bus subscriber on the control-plane), and drop transitional-rot tags.
jpr5
force-pushed
the
feat/http-inprocess-control-plane
branch
from
June 5, 2026 22:48
30c6d2f to
a3ec4f5
Compare
6 tasks
jpr5
added a commit
that referenced
this pull request
Jun 5, 2026
…the fleet (#5287) ## Summary Phase 2 of the harness pool-fleet migration: make the three remaining BROWSER probe families (`e2e_smoke`, `e2e_demos`, `e2e_deep`) actually run on the fleet by wiring producers into the control-plane. The worker `DriverRegistry` for all four browser kinds already landed in #5283; this PR is purely the PRODUCER side. Builds on #5283/#5284/#5285/#5286 (all merged). ### Unit A — three catalog-enumerator factories `createE2eSmokeServiceEnumerator` / `createE2eDemosServiceEnumerator` / `createE2eDeepServiceEnumerator`, each a thin specialization of the generic `createServiceEnumerator` (the #5285 seam) with its own `driverKind` + dashboard `probeKey` prefix and the shared `D6_DISCOVERY_FILTER`: | family | driverKind | probeKey prefix | verified against | |---|---|---|---| | smoke | `e2e_smoke` | `d4:<slug>` | `src/cli/targets.ts` `d4:${slug}` | | demos | `e2e_demos` | `e2e-demos:<slug>` | `config/probes/e2e-demos.yml` `id: e2e-demos` | | deep | `e2e_deep` | `d5-single-pill-e2e:<slug>` | `src/cli/targets.ts` `d5-single-pill-e2e:${slug}` | ### Unit B — multi-schedule wiring in `runControlPlane` `runControlPlane` now builds four producers and passes a `schedules` array to `createControlPlane` (the #5285 multi-schedule API) via a new pure `buildProducerSchedules` helper. Crons are read **literally from the config YAMLs** — the deliberate offsets stagger the four families' Playwright fan-outs on the shared `BrowserPool`: | scheduleId | cron | source | |---|---|---| | `fleet-job-producer` | `40 * * * *` | d6 (unchanged; still honors `FLEET_PRODUCER_CRON`) | | `fleet-producer-e2e-smoke` | `*/15 * * * *` | `e2e-smoke.yml` | | `fleet-producer-e2e-demos` | `10 * * * *` | `e2e-demos.yml` | | `fleet-producer-e2e-deep` | `5,20,35,50 * * * *` | `e2e-deep.yml` | The in-process HTTP probe runner (#5284) and the d6 producer's REQ-B sweep leg are left intact (additive). The worker registry is **not** touched. ### R-timeout (demos) — addressed The demos driver's 20-min outer cap is threaded in-process via the legacy `E2E_DEMOS_TIMEOUT_MS` env, which the **fleet worker never sets** — so without a fix the 38-demo service would blow the driver's 5-min `DEFAULT_TIMEOUT_MS` and go all-red. Fix: - New `E2E_DEMOS_TIMEOUT_MS` SSOT const (mirrors `e2e-demos.yml` `timeout_ms`) in the enumerator module. - `createE2eDemosServiceEnumerator` conveys the cap per-job in `driverInputs.timeout_ms` (smoke/deep convey nothing — verified no `timeout_ms` on their specs, and d6's spec shape is unchanged). - The demos driver now reads `input.timeout_ms` as a resolution source: `ctx.env.E2E_DEMOS_TIMEOUT_MS` (legacy) > `input.timeout_ms` (fleet) > `deps.timeoutMs` > `DEFAULT_TIMEOUT_MS`. Schema gains `timeout_ms: z.number().int().positive().optional()`. ### Risks honored - **R3 pool-contention (deploy-time):** all four families' jobs are claimed by the same pooled worker(s) drawing from ONE `BrowserPool` under `BROWSER_POOL_MAX_CONTEXTS` (24). Per-family `max_concurrency` governs producer ENQUEUE width, not worker execution. The guard is the offset crons + the 24-cap — preserved faithfully here. **Verify at deploy time by triggering each family's producer and confirming the BrowserPool does not starve.** - **R1 context-headers (verified, no change):** both `createPooledE2eSmokeLauncher` and `createPooledE2eDeepLauncher` thread `contextOpts.extraHTTPHeaders`, so smoke + deep set their per-slug `X-AIMock-Context` themselves. ## Test plan - [x] Unit A: `createE2eSmoke/Demos/Deep` factories stamp the right driverKind + probeKey shape and carry driverInputs; demos conveys `timeout_ms` (default + override); d6 equivalence (no `timeout_ms`) asserted — red→green verified. - [x] Demos driver reads `input.timeout_ms` when the env is absent (fleet path), env still wins over input (precedence) — red→green verified. - [x] Unit B: `buildProducerSchedules` emits 4 schedules with exact ids + crons; `FLEET_PRODUCER_CRON` override applies to d6 only; `runControlPlane` registers all 4 producer schedules on the live scheduler alongside the `probe:*` HTTP entries — red→green verified. - [x] Full harness suite green: **120 files / 2128 tests passed**. - [x] `tsc --noEmit` clean except the lone pre-existing `toReversed` error. - [ ] Deploy-time: trigger each family's producer and verify BrowserPool does not starve under co-firing (R3). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
pull Bot
pushed a commit
to TheRakeshPurohit/CopilotKit
that referenced
this pull request
Jun 5, 2026
…schedule producers
Producer-side foundation for fleet item 3b — two behavior-preserving
generalizations, byte-identical for the d6 case:
1. Generalize the d6 service enumerator into a parameterized
`createServiceEnumerator(params)` carrying the service-set filter, the
driverKind, and the probeKey prefix builder. `createD6ServiceEnumerator`
is now a thin wrapper passing the d6 params (e2e_d6 kind, d6:<slug> keys,
D6_DISCOVERY_FILTER), so d6 behavior is unchanged.
2. Generalize createControlPlane to accept an array of
{ scheduleId, cron, producer } entries and register each on the scheduler.
The single-d6 case degenerates to a one-element array on
fleet-job-producer @ 40 * * * *, preserving current behavior.
Out of scope (gated on in-flight PRs): orchestrator runControlPlane wiring
to pass multiple schedules (conflicts with CopilotKit#5284), the e2e_smoke/demos/deep
families (Phase 2), and worker-loop/payload-mapper/probe-loader.
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
Make the fleet control-plane ALSO run the 8 HTTP-only probe families in-process (they don't need the BrowserPool/worker), by lifting the legacy
boot()probe-loader machinery intorunControlPlane. Before this, the control-plane only ran the d6 producer and the 8 HTTP families were dark on the fleet.HTTP-only families now run in-process:
smoke,starter_smoke,image_drift,qa,aimock_wiring,version_drift,pin_drift,redirect_decommission. The browser families (e2e_d6/e2e_smoke/e2e_demos/e2e_deep) are deliberately NOT run in-process — d6 goes via the worker producer path; the rest need a BrowserPool the control-plane does not own. This is independent of the worker-registry / worker-loop work.Design
BROWSER_KINDS(exported) ={e2e_d6, e2e_smoke, e2e_demos, e2e_deep}. HTTP = every kind NOT in this set. Single source of truth for the partition.registerHttpProbeDrivers(exported) registers only the 8 HTTP drivers (no BrowserPool drivers) — kept separate fromregisterAllProbeDriversso the control-plane'sprobeRegistryis HTTP-only.runControlPlane: build an HTTP-onlyprobeRegistry+ a discovery registry wiring the SAME sourcesboot()uses (railway-servicescached 24h with an auth tracker,pnpm-packages), acreateProbeLoaderscoped to HTTP kinds, and the samediffProbeSchedules/buildProbeInvokerloopboot()runs — registering oneprobe:<id>scheduler entry per YAML config on the control-plane's scheduler. Crons are driven FROM the YAML (cfg.schedule), never hardcoded. Each tick flows through the samestatusWriterpipeline as the worker-result aggregator. Hot-reload viaprobeLoader.watchmirrorsboot().includeKindpredicate added tocreateProbeLoader: a browser YAML on disk is SKIPPED (not rejected) against the HTTP-only registry, so a presente2e_*YAML never surfaces a spuriousprobes.reload.failed./health handling
The control-plane now owns in-process probe rules, so
ruleCountreflects the real in-process HTTP probe count (was a hardcoded0). Therole: "control-plane"rules>0 gate-drop is retained (liveness is governed by the scheduler signals already folded intoloopOk), but the real count means a silent probe-loader failure (zero HTTP probes loaded) is now VISIBLE on/healthrather than masked behind the role short-circuit.schedulerJobCountalready counts the newprobe:entries (covers BOTH the producer entry and the probe entries).Teardown: the HTTP-probe file watcher is torn down on
stop()and on bind failure.Red→Green evidence
Wrote failing tests first, confirmed RED against baseline (stashed the impl):
probe:smoke/probe:image_driftnot registered (FAIL)rules< 2 (FAIL)BROWSER_KINDS is not iterable(FAIL)includeKindskip behaviorAfter implementing, all GREEN.
Test plan
runControlPlanein-process HTTP probe tests (scheduling partition + /health rule count + BROWSER_KINDS) — green, RED-verifiedcreateProbeLoaderincludeKindskip-not-reject test — greentsc -p tsconfig.build.json: clean (the only--noEmiterror is the pre-existingtoReversedlib-target issue in an unrelated test file, excluded from the build config)🤖 Generated with Claude Code