[Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

Description

@eddy-curly

Area

apps/server

Steps to reproduce

  1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
    Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
  2. Send any message in that thread while the resume is pending. Real example:
    "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
  3. That message is itself rejected — in this case by a different limit:
    "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
  4. Wait for the scheduled resume time.

Expected behavior

The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

Actual behavior

Auto-resume cancelled: user-took-over.

The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

Root cause

Two defects that compound into a permanently stranded thread.

1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

if(hasPending)return{kind: "skip",reason: "already-pending"};

So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

Proposed fix

Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

The remaining guards already cover the cases this one was reaching for:

  • progressing prevents double-dispatch while a turn is running or starting.
  • awaiting-input still blocks firing into an open approval or user-input prompt.
  • thread-gone still handles deleted / archived / settled threads.

Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

This is the sibling of #6

#6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

Impact

Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

Version or commit

main @ 0a23554

Environment

Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

Logs or stack traces

2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.

All four occurrences of this failure in the local timeline:

2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.

Workaround

Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

      Description

      @eddy-curly

      Area

      apps/server

      Steps to reproduce

      1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
        Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
      2. Send any message in that thread while the resume is pending. Real example:
        "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
      3. That message is itself rejected — in this case by a different limit:
        "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
      4. Wait for the scheduled resume time.

      Expected behavior

      The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

      Actual behavior

      Auto-resume cancelled: user-took-over.

      The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

      Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

      Root cause

      Two defects that compound into a permanently stranded thread.

      1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

      if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

      The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

      2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

      if(hasPending)return{kind: "skip",reason: "already-pending"};

      So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

      Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

      Proposed fix

      Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

      The remaining guards already cover the cases this one was reaching for:

      • progressing prevents double-dispatch while a turn is running or starting.
      • awaiting-input still blocks firing into an open approval or user-input prompt.
      • thread-gone still handles deleted / archived / settled threads.

      Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

      Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

      This is the sibling of #6

      #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

      Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

      Impact

      Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

      Version or commit

      main @ 0a23554

      Environment

      Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

      Logs or stack traces

      2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
      2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
      

      All four occurrences of this failure in the local timeline:

      2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
      2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
      2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
      2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
      

      Workaround

      Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

          Description

          @eddy-curly

          Area

          apps/server

          Steps to reproduce

          1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
            Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
          2. Send any message in that thread while the resume is pending. Real example:
            "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
          3. That message is itself rejected — in this case by a different limit:
            "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
          4. Wait for the scheduled resume time.

          Expected behavior

          The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

          Actual behavior

          Auto-resume cancelled: user-took-over.

          The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

          Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

          Root cause

          Two defects that compound into a permanently stranded thread.

          1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

          if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

          The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

          2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

          if(hasPending)return{kind: "skip",reason: "already-pending"};

          So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

          Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

          Proposed fix

          Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

          The remaining guards already cover the cases this one was reaching for:

          • progressing prevents double-dispatch while a turn is running or starting.
          • awaiting-input still blocks firing into an open approval or user-input prompt.
          • thread-gone still handles deleted / archived / settled threads.

          Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

          Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

          This is the sibling of #6

          #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

          Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

          Impact

          Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

          Version or commit

          main @ 0a23554

          Environment

          Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

          Logs or stack traces

          2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
          2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
          

          All four occurrences of this failure in the local timeline:

          2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
          2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
          2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
          2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
          

          Workaround

          Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

              Description

              @eddy-curly

              Area

              apps/server

              Steps to reproduce

              1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
                Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
              2. Send any message in that thread while the resume is pending. Real example:
                "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
              3. That message is itself rejected — in this case by a different limit:
                "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
              4. Wait for the scheduled resume time.

              Expected behavior

              The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

              Actual behavior

              Auto-resume cancelled: user-took-over.

              The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

              Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

              Root cause

              Two defects that compound into a permanently stranded thread.

              1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

              if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

              The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

              2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

              if(hasPending)return{kind: "skip",reason: "already-pending"};

              So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

              Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

              Proposed fix

              Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

              The remaining guards already cover the cases this one was reaching for:

              • progressing prevents double-dispatch while a turn is running or starting.
              • awaiting-input still blocks firing into an open approval or user-input prompt.
              • thread-gone still handles deleted / archived / settled threads.

              Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

              Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

              This is the sibling of #6

              #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

              Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

              Impact

              Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

              Version or commit

              main @ 0a23554

              Environment

              Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

              Logs or stack traces

              2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
              2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
              

              All four occurrences of this failure in the local timeline:

              2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
              2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
              2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
              2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
              

              Workaround

              Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

                  Description

                  @eddy-curly

                  Area

                  apps/server

                  Steps to reproduce

                  1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
                    Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                  2. Send any message in that thread while the resume is pending. Real example:
                    "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
                  3. That message is itself rejected — in this case by a different limit:
                    "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
                  4. Wait for the scheduled resume time.

                  Expected behavior

                  The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

                  Actual behavior

                  Auto-resume cancelled: user-took-over.

                  The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

                  Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

                  Root cause

                  Two defects that compound into a permanently stranded thread.

                  1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

                  if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

                  The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

                  2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

                  if(hasPending)return{kind: "skip",reason: "already-pending"};

                  So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

                  Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

                  Proposed fix

                  Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

                  The remaining guards already cover the cases this one was reaching for:

                  • progressing prevents double-dispatch while a turn is running or starting.
                  • awaiting-input still blocks firing into an open approval or user-input prompt.
                  • thread-gone still handles deleted / archived / settled threads.

                  Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

                  Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

                  This is the sibling of #6

                  #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

                  Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

                  Impact

                  Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

                  Version or commit

                  main @ 0a23554

                  Environment

                  Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

                  Logs or stack traces

                  2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                  2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
                  

                  All four occurrences of this failure in the local timeline:

                  2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
                  2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
                  2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
                  2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
                  

                  Workaround

                  Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

                      Description

                      @eddy-curly

                      Area

                      apps/server

                      Steps to reproduce

                      1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
                        Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                      2. Send any message in that thread while the resume is pending. Real example:
                        "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
                      3. That message is itself rejected — in this case by a different limit:
                        "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
                      4. Wait for the scheduled resume time.

                      Expected behavior

                      The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

                      Actual behavior

                      Auto-resume cancelled: user-took-over.

                      The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

                      Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

                      Root cause

                      Two defects that compound into a permanently stranded thread.

                      1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

                      if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

                      The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

                      2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

                      if(hasPending)return{kind: "skip",reason: "already-pending"};

                      So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

                      Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

                      Proposed fix

                      Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

                      The remaining guards already cover the cases this one was reaching for:

                      • progressing prevents double-dispatch while a turn is running or starting.
                      • awaiting-input still blocks firing into an open approval or user-input prompt.
                      • thread-gone still handles deleted / archived / settled threads.

                      Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

                      Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

                      This is the sibling of #6

                      #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

                      Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

                      Impact

                      Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

                      Version or commit

                      main @ 0a23554

                      Environment

                      Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

                      Logs or stack traces

                      2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                      2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
                      

                      All four occurrences of this failure in the local timeline:

                      2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
                      2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
                      2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
                      2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
                      

                      Workaround

                      Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

                          Description

                          @eddy-curly

                          Area

                          apps/server

                          Steps to reproduce

                          1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
                            Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                          2. Send any message in that thread while the resume is pending. Real example:
                            "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
                          3. That message is itself rejected — in this case by a different limit:
                            "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
                          4. Wait for the scheduled resume time.

                          Expected behavior

                          The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

                          Actual behavior

                          Auto-resume cancelled: user-took-over.

                          The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

                          Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

                          Root cause

                          Two defects that compound into a permanently stranded thread.

                          1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

                          if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

                          The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

                          2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

                          if(hasPending)return{kind: "skip",reason: "already-pending"};

                          So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

                          Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

                          Proposed fix

                          Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

                          The remaining guards already cover the cases this one was reaching for:

                          • progressing prevents double-dispatch while a turn is running or starting.
                          • awaiting-input still blocks firing into an open approval or user-input prompt.
                          • thread-gone still handles deleted / archived / settled threads.

                          Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

                          Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

                          This is the sibling of #6

                          #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

                          Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

                          Impact

                          Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

                          Version or commit

                          main @ 0a23554

                          Environment

                          Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

                          Logs or stack traces

                          2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                          2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
                          

                          All four occurrences of this failure in the local timeline:

                          2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
                          2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
                          2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
                          2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
                          

                          Workaround

                          Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              [Bug]: auto-resume is cancelled as "user-took-over" by any user message — including one that was itself rejected by a limit #39

                              Description

                              @eddy-curly

                              Area

                              apps/server

                              Steps to reproduce

                              1. Run a Claude thread until a five_hour usage limit rejects it. Auto-resume arms, and the timeline shows:
                                Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                              2. Send any message in that thread while the resume is pending. Real example:
                                "I am going to bed now, ensure you keep going through till you get done with the work that is expected"
                              3. That message is itself rejected — in this case by a different limit:
                                "You've hit your org's monthly spend limit · run /usage-credits to ask your admin for a higher limit"
                              4. Wait for the scheduled resume time.

                              Expected behavior

                              The resume fires. The user asked for continuation and did not turn auto-resume off in the overlay. Per the feature's own contract, only the per-thread toggle should stop a resume.

                              Actual behavior

                              Auto-resume cancelled: user-took-over.

                              The thread is left with no pending resume and never wakes. Confirmed in t3x-auto-resume.json: thread 1b4dacbe-f598-4ffc-9247-df01bbd86a2d ("Cross-check PM and Installer inventories") holds pending: null, with only the previous day's fire in firedAtMs. The arm created at 06:34 is simply gone.

                              Observed 2026-07-31: armed 06:34:38Z, cancelled 10:41:20Z.

                              Root cause

                              Two defects that compound into a permanently stranded thread.

                              1. Any user message cancels the resume.apps/server/src/t3x/autoResume/guards.ts:132:

                              if(newestUserMessageId(thread)!==baseline.newestUserMessageId)return"user-took-over";

                              The baseline is snapshotted when the resume is scheduled, so any user message arriving afterward kills it. The guard infers "the human is driving now" from a message whose literal content was "I am going to bed."

                              2. Nothing can re-arm while a resume is pending.apps/server/src/t3x/autoResume/decide.ts:62:

                              if(hasPending)return{kind: "skip",reason: "already-pending"};

                              So the fresh rejection triggered by the user's message is dropped, and the wake tick then destroys the pre-existing arm. Net pending count afterward: zero. Nothing is left to wake the thread.

                              Additionally: the org monthly spend limit cannot arm auto-resume at all. Detection requires an account.rate-limits.updated runtime event (Reactor.ts:128), which ClaudeAdapter.ts:2908 emits only for SDK rate_limit_event messages, and classifyRateLimit requires a status field. Grepping the repo for spend-limit / usage-credits handling returns nothing. This may warrant a separate issue — and note a monthly cap should probably not be retried on the backoff ladder at all, since it will not reset for days or weeks. At minimum it must not silently destroy a valid five_hour arm.

                              Proposed fix

                              Re-arm rather than cancel. Drop the user-took-over branch and re-baseline onto the newest user message, keeping the pending resume alive.

                              The remaining guards already cover the cases this one was reaching for:

                              • progressing prevents double-dispatch while a turn is running or starting.
                              • awaiting-input still blocks firing into an open approval or user-input prompt.
                              • thread-gone still handles deleted / archived / settled threads.

                              Also narrow already-pending so that a rejection whose computed resume time is later than the current pending one re-schedules instead of being dropped.

                              Only the per-thread overlay toggle (enabled: false, already honoured at Reactor.ts:193) should cancel a pending resume.

                              This is the sibling of #6

                              #6 fixed thread-advanced false-cancellation by requiring positive evidence before cancelling — see the comment at guards.ts:135-140. The user-took-over branch directly above it never got the same treatment.

                              Measured across this install's 46 auto-resume timeline events: 23 armed, 13 fired, 10 cancelled. Six cancellations were the pre-#6thread-advanced bug (all on 2026-07-25). The other four are user-took-over — 2026-07-27 ×3 and 2026-07-31 ×1. Since #6 shipped, roughly 24% of armed resumes (4 of 17) have been lost to this guard.

                              Impact

                              Major degradation or frequent failure. This defeats the entire purpose of auto-resume for overnight work: the moment a user leaves instructions before going to bed, the resume they were relying on is destroyed.

                              Version or commit

                              main @ 0a23554

                              Environment

                              Windows 11 Pro 26200, desktop app, Claude provider (claudeAgent).

                              Logs or stack traces

                              2026-07-31T06:34:38.710Z t3x.auto-resume.scheduled Usage limit reached (five_hour). Auto-resume scheduled in ~247 min.
                              2026-07-31T10:41:20.068Z t3x.auto-resume.cancelled Auto-resume cancelled: user-took-over.
                              

                              All four occurrences of this failure in the local timeline:

                              2026-07-27T00:11:20.295Z Auto-resume cancelled: user-took-over.
                              2026-07-27T17:51:29.395Z Auto-resume cancelled: user-took-over.
                              2026-07-27T23:14:20.755Z Auto-resume cancelled: user-took-over.
                              2026-07-31T10:41:20.068Z Auto-resume cancelled: user-took-over.
                              

                              Workaround

                              Do not type anything in a thread that has a pending resume. Not viable in practice, since the usage-limit banner is exactly the moment a user wants to leave instructions before stepping away.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions