Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving
, '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

Fix orphaned active-work records that block agents - #214

Merged
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work
May 26, 2026
Merged

Fix orphaned active-work records that block agents#214
joryirving merged 2 commits into
mainfrom
fix/orphaned-active-work

Conversation

@joryirving

Copy link
Copy Markdown
Contributor

Summary

Fixes issue #211: Agents can be permanently blocked by orphaned active-work/lease records that reference missing issues.

Changes

Core Fixes

  1. Orphan detection in resolveActiveWork (src/lib/lease.ts)

    • Before returning a resume context, verifies the referenced issue exists in Dispatch's database
    • If the issue is missing/unresolvable, releases the lease and returns hasActiveWork: false
    • Writes an audit log entry for every orphan cleanup
  2. Operator recovery endpoint (src/app/api/agent-work/route.ts)

    • Extended POST /api/agent-work release action to accept agentName + issueId as an alternative to workId/leaseId
    • Added releaseAll: true option to clear all active work for an agent by name
    • Returns releasedLeases and releasedWork counts in the response
    • All releases are audit-logged
  3. Lease ID exposure (src/app/api/agents/[agentName]/active-work/route.ts)

    • Active-work responses now include leaseId in the context for operator recovery via existing release endpoint
  4. Graceful checkpoint/finish handling (src/app/api/agent-work/checkpoint/route.ts, src/app/api/agent-work/finish/route.ts)

    • When no active work is found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of failing with 404

Tests

  • Added tests for orphaned lease detection in resolveActiveWork
  • Added tests for release by agentName + issueId
  • Added tests for releaseAll by agent name
  • Updated existing tests for new mock requirements (issue lookup, audit logging)

Acceptance Criteria Met

  • An agent cannot be blocked forever by active work referencing a missing issue
  • Operators have an API-supported way to clear orphaned work (agentName + issueId or releaseAll)
  • Active-work endpoint never returns a resume context that cannot be acted on
  • Cleanup is audited (all orphan releases write audit log entries)
  • Tests pass

Validation

npm run typecheck # passes
npm run lint # passes
npm test # 851/853 tests pass (2 pre-existing timezone failures in kanban-board.test.tsx)

@its-miso

its-misoBot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Automated recommendation: APPROVE

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic)

PR Review: Fix orphaned active-work records that block agents

Summary

This PR addresses the problem where agents can become permanently blocked by orphaned active-work/lease records that reference missing issues. The implementation introduces orphan detection, cleanup, and operator recovery mechanisms.

Change-by-Change Findings

Core Implementation

  1. src/lib/lease.ts — Added orphan detection in resolveActiveWork:

    • Before returning a resume context, verifies the referenced issue exists via prisma.issue.findUnique
    • If issue is missing, releases the lease, writes an orphan_lease_released audit log entry, and returns null
    • Changed return type from ResumeContext | null to ResumeContextWithLease | null to expose leaseId
    • Added three new helper functions: releaseLeaseByAgentAndIssue, releaseAllLeasesByAgent, releaseAgentWorkByAgentAndIssue
  2. src/app/api/agent-work/route.ts — Extended release endpoint:

    • Original workId/leaseId release path preserved for backward compatibility
    • New agentName + issueId release path for orphan recovery
    • New releaseAll: true path to clear all active work for an agent
    • All new paths write orphan_work_released audit logs
  3. src/app/api/agent-work/checkpoint/route.ts and finish/route.ts — Graceful orphan handling:

    • When no active work found, checks for orphaned leases and releases them
    • Returns { releasedOrphan: true } instead of 404
    • Writes orphan_lease_released_during_checkpoint / orphan_lease_released_during_finish audit logs
  4. src/app/api/agents/[agentName]/active-work/route.ts — Lease ID exposure:

    • Active-work responses now include leaseId via the ResumeContextWithLease type

Test Coverage

Tests were updated/added for:

  • Orphaned lease detection in resolveActiveWork (lease.test.ts)
  • Release by agentName + issueId (agent-work/route.test.ts)
  • Release by releaseAll: true
  • Orphaned lease cleanup in checkpoint/finish endpoints
  • Updated mocks for new issue.findUnique and auditLog.create calls

Standards Compliance

  • ✅ TypeScript types properly defined (ResumeContextWithLease interface)
  • ✅ All state changes write audit log entries with appropriate action types
  • ✅ Input validation before database operations
  • ✅ Error handling with try/catch and appropriate HTTP status codes
  • ✅ Test coverage for new functionality

Linked Issue Fit

Issue #211 (referenced in PR body) states agents can be permanently blocked by orphaned active-work/lease records. The PR addresses this through:

  • Root cause fix: Orphan detection in resolveActiveWork prevents agents from ever receiving a resume context for missing issues
  • Recovery path: New agentName + issueId and releaseAll endpoints for operator cleanup
  • Defensive cleanup: Checkpoint/finish endpoints release orphans when no active work found

All acceptance criteria listed in the PR body are satisfied.

Evidence Provider Findings

No evidence providers were configured for this review.

Tool Harness Findings

No tool requests were executed (mode: plan_execute_once, 0 planned requests).

Unknowns / Needs Verification

None. The PR is well-documented with:

  • Clear summary of changes in the PR body
  • Complete test coverage for new code paths
  • Validation results (typecheck, lint, and 851/853 tests pass)
  • The 2 failing tests are pre-existing timezone-related failures in kanban-board.test.tsx unrelated to this PR

- Update release action error message to 'Missing workId, leaseId, or agentName'
so operators understand agentName is a valid alternative.
- Return leaseId directly from resolveActiveWork to eliminate the second DB query
in the active-work route handler.
@joryirving
joryirving merged commit eb3802d into mainMay 26, 2026
3 checks passed
@joryirving
joryirving deleted the fix/orphaned-active-work branch May 26, 2026 02:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@joryirving