feat(spine): estate watch — the whole portfolio on one spine #5538
3 changed files+56−1
Modified.env.example+6−0View fileUnifiedSplit
@@ -353,3 +353,9 @@ PEER_WATCH_URL=
353353"Signal precision" section. Verdicts collect regardless; site admins
354354always see the section. Flip after verified-before-shown ships.
355355SIGNAL_PRECISION_PUBLIC=
356
357# Estate watch: comma-separated absolute URLs of portfolio products this
358spine monitors (each becomes an estate:<host> check with transition
359paging). Replaces per-repo scheduled GitHub workflow "heartbeats".
360Production: https://vapron.ai/,https://alecrae.com/,https://davenroe.com/,https://www.zoobicon.com/,https://gatetest.ai/
361ESTATE_WATCH_URLS=
Modifiedsrc/__tests__/spine-peer-watch.test.ts+16−0View fileUnifiedSplit
@@ -49,3 +49,19 @@ describe("webhook events carry Slack's required text field", () => {
4949 expect(e.text).toContain("https://gluecron.com/admin/spine");
5050 });
5151});
52
53describe("estateWatchChecks — the portfolio on one spine", () => {
54 test("off when unset; parses a comma list into estate:<host> checks", async () => {
55 const { estateWatchChecks } = await import("../lib/synthetic-monitor");
56 expect(estateWatchChecks({})).toEqual([]);
57 const checks = estateWatchChecks({
58 ESTATE_WATCH_URLS: "https://alecrae.com/, https://www.zoobicon.com/ ,garbage, http://davenroe.com/x",
59 });
60 expect(checks.map((c) => c.name)).toEqual([
61 "estate:alecrae.com",
62 "estate:www.zoobicon.com",
63 "estate:davenroe.com",
64 ]);
65 expect(checks.every((c) => c.timeoutMs === 8000)).toBe(true);
66 });
67});
Modifiedsrc/lib/synthetic-monitor.ts+34−1View fileUnifiedSplit
@@ -210,6 +210,38 @@ export function peerWatchChecks(
210210 return [{ name: "peer:watch", url, timeoutMs: 8000 }];
211211}
212212
213/**
214 * Estate watch (2026-08-26): every product in the portfolio, monitored by
215 * THIS spine instead of a scheduled GitHub workflow per repo. Discovered
216 * via the owner's 24k-unread inbox: AlecRae/DavenRoe/Zoobicon each ran a
217 * failing "production heartbeat"/"synthetic checks" Action every ~15 min —
218 * billed minutes, continuous failure email, and all three products were
219 * UP the whole time (probed 2026-08-26: 200s under 600ms). The monitoring
220 * was broken, not the products; false positives at inbox scale.
221 *
222 * ESTATE_WATCH_URLS: comma-separated absolute URLs. Each becomes an
223 * `estate:<host>` check riding the same transition alerting, incident
224 * log, medic doctrine, and paging webhook as everything else — one page
225 * per real edge, recovery notices, no repeats. Off when unset.
226 */
227export function estateWatchChecks(
228 env: Record<string, string | undefined> = process.env
229): SyntheticCheckSpec[] {
230 const raw = (env.ESTATE_WATCH_URLS || "").trim();
231 if (!raw) return [];
232 const out: SyntheticCheckSpec[] = [];
233 for (const part of raw.split(",")) {
234 const url = part.trim();
235 if (!/^https?:\/\//i.test(url)) continue;
236 try {
237 out.push({ name: `estate:${new URL(url).host}`, url, timeoutMs: 8000 });
238 } catch {
239 /* malformed entry — skip, never throw into the tick */
240 }
241 }
242 return out;
243}
244
213245export async function runSyntheticChecks(opts?: {
214246 baseUrl?: string;
215247 fetchImpl?: typeof fetch;
@@ -217,7 +249,8 @@ export async function runSyntheticChecks(opts?: {
217249}): Promise<SyntheticCheckResult[]> {
218250 const baseUrl = opts?.baseUrl ?? config.appBaseUrl;
219251 const fetchImpl = opts?.fetchImpl ?? fetch;
220 const checks = opts?.checks ?? [...SYNTHETIC_CHECKS, ...peerWatchChecks()];
252 const checks =
253 opts?.checks ?? [...SYNTHETIC_CHECKS, ...peerWatchChecks(), ...estateWatchChecks()];
221254 return Promise.all(checks.map((c) => runOneCheck(c, baseUrl, fetchImpl)));
222255}
223256
224257
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts