{"record":{"id":"ef94463eba61cbb8","repo":"koala73/worldmonitor","slug":"resumeramp-no-ramp-configured","errorCode":null,"errorMessage":"[resumeRamp] no ramp configured","messagePattern":"\\[resumeRamp\\] no ramp configured","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":233,"sourceCode":"    return { ok: true };\n  },\n});\n\nexport const pauseRamp = internalMutation({\n  args: {},\n  handler: async (ctx) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[pauseRamp] no ramp configured\");\n    await ctx.db.patch(row._id, { active: false });\n    return { ok: true, prevActive: row.active };\n  },\n});\n\nexport const resumeRamp = internalMutation({\n  args: {},\n  handler: async (ctx) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[resumeRamp] no ramp configured\");\n    if (row.killGateTripped) {\n      throw new Error(\n        \"[resumeRamp] kill-gate is tripped; clearKillGate first after investigating.\",\n      );\n    }\n    await ctx.db.patch(row._id, { active: true });\n    return { ok: true };\n  },\n});\n\nexport const clearKillGate = internalMutation({\n  args: { reason: v.string() },\n  handler: async (ctx, { reason }) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[clearKillGate] no ramp configured\");\n    if (!row.killGateTripped) {\n      return { ok: true, noop: true };\n    }","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/rampRunner.ts#L215-L251","documentation":"resumeRamp is a Convex internalMutation that sets broadcastRampConfig.active=true. Like the other admin mutations it reads the singleton row via loadConfig(ctx) and throws if it is absent, so resuming a non-existent ramp fails loudly instead of implying success.","triggerScenarios":"Calling resumeRamp before initRamp, after abortRamp deleted the row, or in an unseeded Convex deployment/preview.","commonSituations":"Staging/preview deployment never seeded; operator aborted the ramp and an automation later tries to resume it; client connected to the wrong Convex project.","solutions":["Preflight with getRampStatus; if {configured:false}, run initRamp before resumeRamp.","If abortRamp was used, re-create the config via initRamp with the intended rampCurve and seed fields, then resume.","Verify the caller targets the Convex deployment that actually holds the config row."],"exampleFix":"// before\nawait ctx.runMutation(internal.broadcast.rampRunner.resumeRamp, {});\n\n// after\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) throw new Error('Cannot resume: ramp not configured. Run initRamp first.');\nawait ctx.runMutation(internal.broadcast.rampRunner.resumeRamp, {});","handlingStrategy":"validation","validationCode":"// Preflight before resumeRamp\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) {\n  throw new Error('Ramp not configured — run initRamp before resumeRamp.');\n}","typeGuard":"function isConfigured(s: { configured: boolean }): s is { configured: true; active: boolean; killGateTripped: boolean } {\n  return s.configured === true;\n}","tryCatchPattern":null,"preventionTips":["Preflight resumeRamp with getRampStatus and verify configured === true.","Ensure each deployment is seeded via initRamp before any resume automation runs.","Abort-aware runbooks should short-circuit when getRampStatus reports configured:false."],"tags":["convex","broadcast-ramp","state-missing","operator-error"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}