{"record":{"id":"0c8ac9ed0bea28c8","repo":"koala73/worldmonitor","slug":"activation-outcome-buckets-must-be-disjoint-and-co","errorCode":null,"errorMessage":"activation outcome buckets must be disjoint and contain at most ${proActivationStepIdValidator.members.length} steps","messagePattern":"activation outcome buckets must be disjoint and contain at most (.+?) steps","errorType":"validation","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/billing.ts","lineNumber":997,"sourceCode":"      !Number.isSafeInteger(args.revision) ||\n      args.revision < 1 ||\n      args.revision > MAX_PRO_ACTIVATION_OUTCOME_REVISION\n    ) {\n      throw new ConvexError(\n        `activation outcome revision must be an integer from 1 to ${MAX_PRO_ACTIVATION_OUTCOME_REVISION}`,\n      );\n    }\n    const allSteps = [\n      ...args.confirmedSteps,\n      ...args.skippedSteps,\n      ...(args.blockedSteps ?? []),\n      ...args.failedSteps,\n    ];\n    if (\n      allSteps.length > proActivationStepIdValidator.members.length ||\n      new Set(allSteps).size !== allSteps.length\n    ) {\n      throw new ConvexError(\n        `activation outcome buckets must be disjoint and contain at most ${proActivationStepIdValidator.members.length} steps`,\n      );\n    }\n    if (args.revision <= (presentation.outcomeRevision ?? 0)) {\n      return false;\n    }\n\n    const now = Date.now();\n    await ctx.db.patch(presentation._id, {\n      confirmedSteps: args.confirmedSteps,\n      skippedSteps: args.skippedSteps,\n      // Written straight from the arg, NOT coerced to []. Convex removes a\n      // field patched as `undefined`, so a client too old to report blocked\n      // steps leaves the field ABSENT (\"could not report\") instead of claiming\n      // \"none were blocked\" — a claim that is actively false for that client,\n      // which classified denials as skips. Absent is also what lets an analyst\n      // exclude those rows from a denial rate. Every snapshot stays a full\n      // replacement either way: an explicit [] clears, and so does absence.","sourceCodeStart":979,"sourceCodeEnd":1015,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/convex/payments/billing.ts#L979-L1015","documentation":"ConvexError guard validating the Pro activation outcome step buckets: confirmedSteps, skippedSteps, blockedSteps, and failedSteps combined must contain no duplicates and no more entries than the total number of known activation steps. Fires when a client double-reports a step in two buckets or fabricates unknown/duplicate step ids.","triggerScenarios":"Thrown at convex/payments/billing.ts:981 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Place each step id in exactly one outcome bucket per revision","Use only step ids from proActivationStepIdValidator's known members","Rebuild the buckets client-side from the actual per-step outcomes before submitting"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}