Commit5e67f6b
fix(vapron): correct receiver URL — /api/hooks/gluecron/push (old path 404s)
fix(vapron): correct receiver URL — /api/hooks/gluecron/push (old path 404s) Verified live 2026-07-14: Vapron's actual receiver is POST https://vapron.ai/api/hooks/gluecron/push (401 on unsigned payloads = route exists). Gluecron's default, wire-contract comment, panel helper text, and test pins all referenced the pre-move /api/webhooks/gluecron-push, which now 404s — so default-configured deploy webhooks died silently in retries. Test pin inverted so a revert fails loudly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 files changed+12−75e67f6bf68c1f82edd36af4f2770b9f0d1f5b967
4 changed files+12−7
Modifiedsrc/__tests__/vapron-deploy.test.ts+5−4View fileUnifiedSplit
@@ -136,16 +136,17 @@ describe("hooks/post-receive — triggerVapronDeploy (BLK-016 sender)", () => {
136136 expect(typeof triggerVapronDeploy).toBe("function");
137137 });
138138
139 it("POSTs to /api/webhooks/gluecron-push (matches Vapron receiver path)", async () => {
139 it("POSTs to /api/hooks/gluecron/push (matches Vapron receiver path, verified live 2026-07-14)", async () => {
140140 const { calls, fn } = captureFetch();
141141
142142 await triggerVapronDeploy(makeArgs(), { fetchImpl: fn, sleep: noSleep });
143143
144144 expect(calls.length).toBe(1);
145145 expect(calls[0]!.url).toBe(
146 "https://vapron.ai/api/webhooks/gluecron-push"
146 "https://vapron.ai/api/hooks/gluecron/push"
147147 );
148 expect(calls[0]!.url).not.toContain("/api/hooks/gluecron/push");
148 // The pre-move path 404s on Vapron now — pinned so a revert fails loudly.
149 expect(calls[0]!.url).not.toContain("/api/webhooks/gluecron-push");
149150 expect(calls[0]!.init.method).toBe("POST");
150151 });
151152
@@ -368,7 +369,7 @@ describe("vapron config — legacy CRONTECH_* env fallback", () => {
368369 });
369370
370371 it("defaults to the vapron.ai webhook URL and ccantynz-alt/vapron repo", () => {
371 expect(config.vapronDeployUrl).toBe("https://vapron.ai/api/webhooks/gluecron-push");
372 expect(config.vapronDeployUrl).toBe("https://vapron.ai/api/hooks/gluecron/push");
372373 expect(config.vapronRepo).toBe("ccantynz-alt/vapron");
373374 });
374375
Modifiedsrc/hooks/post-receive.ts+3−1View fileUnifiedSplit
@@ -406,7 +406,9 @@ export function __setSelfHostSpawnForTests(
406406 *
407407 * Wire contract (matches Vapron's `apps/api/src/webhooks/gluecron-push.ts`):
408408 *
409 * POST https://vapron.ai/api/webhooks/gluecron-push
409 * POST https://vapron.ai/api/hooks/gluecron/push
410 * (path verified live 2026-07-14 — the receiver returns 401 on unsigned
411 * payloads; the pre-move /api/webhooks/gluecron-push now 404s)
410412 * Content-Type: application/json
411413 * X-Gluecron-Signature: sha256=<hex(hmac-sha256(body, GLUECRON_WEBHOOK_SECRET))>
412414 *
Modifiedsrc/lib/config.ts+3−1View fileUnifiedSplit
@@ -20,7 +20,9 @@ export const config = {
2020 return (
2121 process.env.VAPRON_DEPLOY_URL ||
2222 process.env.CRONTECH_DEPLOY_URL || // legacy name (pre-rename)
23 "https://vapron.ai/api/webhooks/gluecron-push"
23 // Verified live 2026-07-14: Vapron's receiver is /api/hooks/gluecron/push
24 // (the old /api/webhooks/gluecron-push 404s — Vapron moved it).
25 "https://vapron.ai/api/hooks/gluecron/push"
2426 );
2527 },
2628 /**
Modifiedsrc/lib/system-config.ts+1−1View fileUnifiedSplit
@@ -255,7 +255,7 @@ export const INTEGRATION_FIELDS: IntegrationField[] = [
255255 envFallback: "VAPRON_DEPLOY_URL",
256256 label: "Vapron webhook URL",
257257 helper:
258 "Optional: notify Vapron (formerly Crontech) on every push to the canonical repo. Default: https://vapron.ai/api/webhooks/gluecron-push",
258 "Optional: notify Vapron (formerly Crontech) on every push to the canonical repo. Default: https://vapron.ai/api/hooks/gluecron/push",
259259 isSecret: false,
260260 group: "webhook",
261261 },
262262