{"record":{"id":"ba8fd85e7fd17cbf","repo":"ruvnet/ruflo","slug":"claim-is-still-in-grace-period","errorCode":null,"errorMessage":"Claim is still in grace period","messagePattern":"Claim is still in grace period","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"v3/@claude-flow/claims/src/application/work-stealing-service.ts","lineNumber":221,"sourceCode":"\n  /**\n   * Mark work as stealable with the given reason\n   */\n  async markStealable(issueId: IssueId, info: StealableInfo): Promise<void> {\n    const claim = await this.repository.findByIssueId(issueId);\n\n    if (!claim) {\n      throw new Error(`Claim not found for issue: ${issueId}`);\n    }\n\n    // Check if already stealable\n    if (claim.stealInfo) {\n      return; // Already marked\n    }\n\n    // Check grace period protection\n    if (this.isInGracePeriod(claim)) {\n      throw new Error(`Claim is still in grace period`);\n    }\n\n    // Check progress protection\n    if (this.isProtectedByProgress(claim)) {\n      throw new Error(`Claim is protected by progress (${claim.progress}%)`);\n    }\n\n    // Update claim with stealable info\n    const now = new Date();\n    claim.stealInfo = {\n      ...info,\n      markedAt: now,\n    };\n    claim.stealableAt = now;\n\n    await this.repository.update(claim);\n\n    // Emit event","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/claims/src/application/work-stealing-service.ts#L203-L239","documentation":"Plain Error thrown by markStealable when isInGracePeriod(claim) is true. Newly created claims are protected by a grace window (configured in WorkStealingConfig) so agents are not relieved of work the instant they claim it. The claim must age past the grace period before it can be marked stealable.","triggerScenarios":"Calling markStealable immediately or very soon after a claim is created; an auto-detect loop whose interval is shorter than the grace period; gracePeriod misconfigured to a large value.","commonSituations":"Grace period set too long relative to expected task duration; tight polling loop; clock skew when an HLC is wired in (nowMs uses hlc.physicalMs).","solutions":["Wait until the grace window elapses before retrying markStealable.","Lower config.gracePeriodMinutes (or equivalent field in DEFAULT_WORK_STEALING_CONFIG) if the default is too conservative for your workload.","Schedule the mark for now + remaining grace time instead of polling in a tight loop.","In auto-mark loops, skip claims still in grace rather than treating them as errors."],"exampleFix":"// before\nawait workStealing.markStealable(issueId, info); // freshly claimed -> throws\n\n// after\nconst claim = await repo.findByIssueId(issueId);\nconst graceMs = config.gracePeriodMinutes * 60_000;\nif (Date.now() - claim.createdAt.getTime() >= graceMs) {\n  await workStealing.markStealable(issueId, info);\n} else {\n  scheduleForLater(issueId, graceMs - (Date.now() - claim.createdAt.getTime()));\n}","handlingStrategy":"retry","validationCode":"const claim = await repository.findByIssueId(issueId);\nconst elapsedMs = Date.now() - (claim?.createdAt?.getTime() ?? 0);\nconst graceMs = config.gracePeriodMinutes * 60_000;\nif (elapsedMs < graceMs) {\n  // not eligible yet; schedule for later\n  scheduleMark(issueId, graceMs - elapsedMs);\n  return;\n}\nawait workStealing.markStealable(issueId, info);","typeGuard":null,"tryCatchPattern":"try {\n  await workStealing.markStealable(issueId, info);\n} catch (e) {\n  if (e instanceof Error && /grace period/.test(e.message)) {\n    // transient: retry after the remaining grace window\n    return scheduleMark(issueId, retryAfterMs);\n  }\n  throw e;\n}","preventionTips":["Set the auto-detect interval longer than config.gracePeriodMinutes.","Compute eligibility from claim.createdAt before attempting to mark.","Account for HLC physicalMs vs wall-clock when in federated mode."],"tags":["work-stealing","grace-period","timing","plain-error"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}