fix: prove continuation origin through the real dispatch receipt

Exercise the committed status outbox through production dispatch and run claim. Follow the coalesced intent to its admitted dispatch, retain requester checks, and reject a missing dispatch. Refresh the generated skill inventory after the guidance change.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-20 20:52:33 -05:00
1 parent 7179058e39
commit b8ca59c0a6
9 files changed
+282 -235

No files matched your search

@@ -47,34 +47,34 @@ The skill/reference inventory and eval cases are the only normative behavior sou
| skill:skills/paperclip/SKILL.md:conversation-tasks:32 | optional_agent_tool | skills/paperclip/SKILL.md:32 |
| skill:skills/paperclip/SKILL.md:server-verified-external-chat-turns:49 | control_plane_owned | skills/paperclip/SKILL.md:49 |
| skill:skills/paperclip/SKILL.md:the-heartbeat-procedure:89 | optional_agent_tool | skills/paperclip/SKILL.md:89 |
| skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:200 | always_agent_tool | skills/paperclip/SKILL.md:200 |
| skill:skills/paperclip/SKILL.md:status-quick-guide:248 | control_plane_owned | skills/paperclip/SKILL.md:248 |
| skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:258 | optional_agent_tool | skills/paperclip/SKILL.md:258 |
| skill:skills/paperclip/SKILL.md:delegating-review-tasks:271 | always_agent_tool | skills/paperclip/SKILL.md:271 |
| skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:282 | control_plane_owned | skills/paperclip/SKILL.md:282 |
| skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:290 | control_plane_owned | skills/paperclip/SKILL.md:290 |
| skill:skills/paperclip/SKILL.md:requesting-board-approval:315 | optional_agent_tool | skills/paperclip/SKILL.md:315 |
| skill:skills/paperclip/SKILL.md:issue-thread-interactions:336 | optional_agent_tool | skills/paperclip/SKILL.md:336 |
| skill:skills/paperclip/SKILL.md:standalone-decisions:366 | optional_agent_tool | skills/paperclip/SKILL.md:366 |
| skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:470 | optional_agent_tool | skills/paperclip/SKILL.md:470 |
| skill:skills/paperclip/SKILL.md:niche-workflow-pointers:512 | optional_agent_tool | skills/paperclip/SKILL.md:512 |
| skill:skills/paperclip/SKILL.md:cases:522 | optional_agent_tool | skills/paperclip/SKILL.md:522 |
| skill:skills/paperclip/SKILL.md:company-skills-workflow:527 | optional_agent_tool | skills/paperclip/SKILL.md:527 |
| skill:skills/paperclip/SKILL.md:routines:538 | optional_agent_tool | skills/paperclip/SKILL.md:538 |
| skill:skills/paperclip/SKILL.md:task-files-and-workspace-choices:549 | optional_agent_tool | skills/paperclip/SKILL.md:549 |
| skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:555 | optional_agent_tool | skills/paperclip/SKILL.md:555 |
| skill:skills/paperclip/SKILL.md:proposing-credentials-safely:562 | optional_agent_tool | skills/paperclip/SKILL.md:562 |
| skill:skills/paperclip/SKILL.md:reading-granted-secrets:569 | optional_agent_tool | skills/paperclip/SKILL.md:569 |
| skill:skills/paperclip/SKILL.md:critical-rules:595 | optional_agent_tool | skills/paperclip/SKILL.md:595 |
| skill:skills/paperclip/SKILL.md:comment-style-required:616 | always_agent_tool | skills/paperclip/SKILL.md:616 |
| skill:skills/paperclip/SKILL.md:update:648 | optional_agent_tool | skills/paperclip/SKILL.md:648 |
| skill:skills/paperclip/SKILL.md:task-documents-and-deliverables:658 | always_agent_tool | skills/paperclip/SKILL.md:658 |
| skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:672 | optional_agent_tool | skills/paperclip/SKILL.md:672 |
| skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:703 | optional_agent_tool | skills/paperclip/SKILL.md:703 |
| skill:skills/paperclip/SKILL.md:searching-issues:733 | optional_agent_tool | skills/paperclip/SKILL.md:733 |
| skill:skills/paperclip/SKILL.md:full-reference:743 | optional_agent_tool | skills/paperclip/SKILL.md:743 |
| skill:skills/paperclip/SKILL.md:conversational-confirmation-answers:747 | always_agent_tool | skills/paperclip/SKILL.md:747 |
| skill:skills/paperclip/SKILL.md:incidental-feedback:764 | optional_agent_tool | skills/paperclip/SKILL.md:764 |
| skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:201 | always_agent_tool | skills/paperclip/SKILL.md:201 |
| skill:skills/paperclip/SKILL.md:status-quick-guide:249 | control_plane_owned | skills/paperclip/SKILL.md:249 |
| skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:259 | optional_agent_tool | skills/paperclip/SKILL.md:259 |
| skill:skills/paperclip/SKILL.md:delegating-review-tasks:272 | always_agent_tool | skills/paperclip/SKILL.md:272 |
| skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:283 | control_plane_owned | skills/paperclip/SKILL.md:283 |
| skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:291 | control_plane_owned | skills/paperclip/SKILL.md:291 |
| skill:skills/paperclip/SKILL.md:requesting-board-approval:316 | optional_agent_tool | skills/paperclip/SKILL.md:316 |
| skill:skills/paperclip/SKILL.md:issue-thread-interactions:337 | optional_agent_tool | skills/paperclip/SKILL.md:337 |
| skill:skills/paperclip/SKILL.md:standalone-decisions:367 | optional_agent_tool | skills/paperclip/SKILL.md:367 |
| skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:471 | optional_agent_tool | skills/paperclip/SKILL.md:471 |
| skill:skills/paperclip/SKILL.md:niche-workflow-pointers:513 | optional_agent_tool | skills/paperclip/SKILL.md:513 |
| skill:skills/paperclip/SKILL.md:cases:523 | optional_agent_tool | skills/paperclip/SKILL.md:523 |
| skill:skills/paperclip/SKILL.md:company-skills-workflow:528 | optional_agent_tool | skills/paperclip/SKILL.md:528 |
| skill:skills/paperclip/SKILL.md:routines:539 | optional_agent_tool | skills/paperclip/SKILL.md:539 |
| skill:skills/paperclip/SKILL.md:task-files-and-workspace-choices:550 | optional_agent_tool | skills/paperclip/SKILL.md:550 |
| skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:558 | optional_agent_tool | skills/paperclip/SKILL.md:558 |
| skill:skills/paperclip/SKILL.md:proposing-credentials-safely:565 | optional_agent_tool | skills/paperclip/SKILL.md:565 |
| skill:skills/paperclip/SKILL.md:reading-granted-secrets:572 | optional_agent_tool | skills/paperclip/SKILL.md:572 |
| skill:skills/paperclip/SKILL.md:critical-rules:598 | optional_agent_tool | skills/paperclip/SKILL.md:598 |
| skill:skills/paperclip/SKILL.md:comment-style-required:619 | always_agent_tool | skills/paperclip/SKILL.md:619 |
| skill:skills/paperclip/SKILL.md:update:651 | optional_agent_tool | skills/paperclip/SKILL.md:651 |
| skill:skills/paperclip/SKILL.md:task-documents-and-deliverables:661 | always_agent_tool | skills/paperclip/SKILL.md:661 |
| skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:675 | optional_agent_tool | skills/paperclip/SKILL.md:675 |
| skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:706 | optional_agent_tool | skills/paperclip/SKILL.md:706 |
| skill:skills/paperclip/SKILL.md:searching-issues:736 | optional_agent_tool | skills/paperclip/SKILL.md:736 |
| skill:skills/paperclip/SKILL.md:full-reference:746 | optional_agent_tool | skills/paperclip/SKILL.md:746 |
| skill:skills/paperclip/SKILL.md:conversational-confirmation-answers:750 | always_agent_tool | skills/paperclip/SKILL.md:750 |
| skill:skills/paperclip/SKILL.md:incidental-feedback:767 | optional_agent_tool | skills/paperclip/SKILL.md:767 |
| skill:skills/paperclip/references/api-reference.md:paperclip-api-reference:1 | optional_agent_tool | skills/paperclip/references/api-reference.md:1 |
| skill:skills/paperclip/references/api-reference.md:response-schemas:9 | optional_agent_tool | skills/paperclip/references/api-reference.md:9 |
| skill:skills/paperclip/references/api-reference.md:agent-record-get-api-agents-me-or-get-api-agents-agentid:11 | optional_agent_tool | skills/paperclip/references/api-reference.md:11 |
@@ -56,252 +56,252 @@
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:200",
"id": "skill:skills/paperclip/SKILL.md:201",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L200:generated-artifacts-and-work-products",
"sourceAnchor": "skills/paperclip/SKILL.md#L201:generated-artifacts-and-work-products",
"heading": "Generated Artifacts and Work Products",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "register_deliverable",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:248",
"id": "skill:skills/paperclip/SKILL.md:249",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L248:status-quick-guide",
"sourceAnchor": "skills/paperclip/SKILL.md#L249:status-quick-guide",
"heading": "Status Quick Guide",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:258",
"id": "skill:skills/paperclip/SKILL.md:259",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L258:monitors-and-watchers-say-only-what-you-actually-scheduled",
"sourceAnchor": "skills/paperclip/SKILL.md#L259:monitors-and-watchers-say-only-what-you-actually-scheduled",
"heading": "Monitors and Watchers (say only what you actually scheduled)",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:271",
"id": "skill:skills/paperclip/SKILL.md:272",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L271:delegating-review-tasks",
"sourceAnchor": "skills/paperclip/SKILL.md#L272:delegating-review-tasks",
"heading": "Delegating review tasks",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:282",
"id": "skill:skills/paperclip/SKILL.md:283",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L282:managing-a-user-s-inbox",
"sourceAnchor": "skills/paperclip/SKILL.md#L283:managing-a-user-s-inbox",
"heading": "Managing A User's Inbox",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:290",
"id": "skill:skills/paperclip/SKILL.md:291",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L290:issue-dependencies-blockers",
"sourceAnchor": "skills/paperclip/SKILL.md#L291:issue-dependencies-blockers",
"heading": "Issue Dependencies (Blockers)",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:315",
"id": "skill:skills/paperclip/SKILL.md:316",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L315:requesting-board-approval",
"sourceAnchor": "skills/paperclip/SKILL.md#L316:requesting-board-approval",
"heading": "Requesting Board Approval",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:336",
"id": "skill:skills/paperclip/SKILL.md:337",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L336:issue-thread-interactions",
"sourceAnchor": "skills/paperclip/SKILL.md#L337:issue-thread-interactions",
"heading": "Issue-Thread Interactions",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "request_human_input",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:366",
"id": "skill:skills/paperclip/SKILL.md:367",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L366:standalone-decisions",
"sourceAnchor": "skills/paperclip/SKILL.md#L367:standalone-decisions",
"heading": "Standalone Decisions",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:470",
"id": "skill:skills/paperclip/SKILL.md:471",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L470:mcp-tool-approval-gates",
"sourceAnchor": "skills/paperclip/SKILL.md#L471:mcp-tool-approval-gates",
"heading": "MCP Tool Approval Gates",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:512",
"id": "skill:skills/paperclip/SKILL.md:513",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L512:niche-workflow-pointers",
"sourceAnchor": "skills/paperclip/SKILL.md#L513:niche-workflow-pointers",
"heading": "Niche Workflow Pointers",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:522",
"id": "skill:skills/paperclip/SKILL.md:523",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L522:cases",
"sourceAnchor": "skills/paperclip/SKILL.md#L523:cases",
"heading": "Cases",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:527",
"id": "skill:skills/paperclip/SKILL.md:528",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L527:company-skills-workflow",
"sourceAnchor": "skills/paperclip/SKILL.md#L528:company-skills-workflow",
"heading": "Company Skills Workflow",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:538",
"id": "skill:skills/paperclip/SKILL.md:539",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L538:routines",
"sourceAnchor": "skills/paperclip/SKILL.md#L539:routines",
"heading": "Routines",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:549",
"id": "skill:skills/paperclip/SKILL.md:550",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L549:task-files-and-workspace-choices",
"sourceAnchor": "skills/paperclip/SKILL.md#L550:task-files-and-workspace-choices",
"heading": "Task Files and Workspace Choices",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:555",
"id": "skill:skills/paperclip/SKILL.md:558",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L555:issue-workspace-runtime-controls",
"sourceAnchor": "skills/paperclip/SKILL.md#L558:issue-workspace-runtime-controls",
"heading": "Issue Workspace Runtime Controls",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:562",
"id": "skill:skills/paperclip/SKILL.md:565",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L562:proposing-credentials-safely",
"sourceAnchor": "skills/paperclip/SKILL.md#L565:proposing-credentials-safely",
"heading": "Proposing Credentials Safely",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:569",
"id": "skill:skills/paperclip/SKILL.md:572",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L569:reading-granted-secrets",
"sourceAnchor": "skills/paperclip/SKILL.md#L572:reading-granted-secrets",
"heading": "Reading Granted Secrets",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:595",
"id": "skill:skills/paperclip/SKILL.md:598",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L595:critical-rules",
"sourceAnchor": "skills/paperclip/SKILL.md#L598:critical-rules",
"heading": "Critical Rules",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:616",
"id": "skill:skills/paperclip/SKILL.md:619",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L616:comment-style-required",
"sourceAnchor": "skills/paperclip/SKILL.md#L619:comment-style-required",
"heading": "Comment Style (Required)",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "report_progress",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:648",
"id": "skill:skills/paperclip/SKILL.md:651",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L648:update",
"sourceAnchor": "skills/paperclip/SKILL.md#L651:update",
"heading": "Update",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:658",
"id": "skill:skills/paperclip/SKILL.md:661",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L658:task-documents-and-deliverables",
"sourceAnchor": "skills/paperclip/SKILL.md#L661:task-documents-and-deliverables",
"heading": "Task Documents and Deliverables",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "write_document",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:672",
"id": "skill:skills/paperclip/SKILL.md:675",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L672:planning-required-when-planning-requested",
"sourceAnchor": "skills/paperclip/SKILL.md#L675:planning-required-when-planning-requested",
"heading": "Planning (Required when planning requested)",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "write_document",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:703",
"id": "skill:skills/paperclip/SKILL.md:706",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L703:key-endpoints-hot-routes",
"sourceAnchor": "skills/paperclip/SKILL.md#L706:key-endpoints-hot-routes",
"heading": "Key Endpoints (Hot Routes)",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:733",
"id": "skill:skills/paperclip/SKILL.md:736",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L733:searching-issues",
"sourceAnchor": "skills/paperclip/SKILL.md#L736:searching-issues",
"heading": "Searching Issues",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:743",
"id": "skill:skills/paperclip/SKILL.md:746",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L743:full-reference",
"sourceAnchor": "skills/paperclip/SKILL.md#L746:full-reference",
"heading": "Full Reference",
"primaryDisposition": "control_plane_owned",
"semanticOperation": "runtime_reconciliation",
"expectedMockState": "runtime_decision_record"
},
{
"id": "skill:skills/paperclip/SKILL.md:747",
"id": "skill:skills/paperclip/SKILL.md:750",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L747:conversational-confirmation-answers",
"sourceAnchor": "skills/paperclip/SKILL.md#L750:conversational-confirmation-answers",
"heading": "Conversational confirmation answers",
"primaryDisposition": "always_agent_tool",
"semanticOperation": "call_api",
"expectedMockState": "operation_result"
},
{
"id": "skill:skills/paperclip/SKILL.md:764",
"id": "skill:skills/paperclip/SKILL.md:767",
"kind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md#L764:incidental-feedback",
"sourceAnchor": "skills/paperclip/SKILL.md#L767:incidental-feedback",
"heading": "Incidental feedback",
"primaryDisposition": "optional_agent_tool",
"semanticOperation": "scoped_discovery",
@@ -5,6 +5,6 @@ Generated by `scripts/generate-capability-contract.mjs`; do not edit generated f
- Skill/reference headings: 167
- Legacy MCP tools: 42
- Eval cases: 106 across 16 groups
- Deterministic content SHA-256: `56f834a342a1e75e535d1f1a23f483882331d1fb364aa6bd17de7d9a2da39c4b`
- Deterministic content SHA-256: `05f5724a98ae5e6045a550d969599c3e5f40d4c97069ae2453cc6e7e60d2ba44`
Every row has exactly one primary disposition, a source anchor, a semantic operation, and a mock-state expectation.
@@ -105,9 +105,9 @@
]
},
{
"id": "skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:200",
"id": "skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:201",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:200",
"sourceAnchor": "skills/paperclip/SKILL.md:201",
"title": "Generated Artifacts and Work Products",
"expectedSemantics": "Skill guidance headed “Generated Artifacts and Work Products”.",
"primaryDisposition": "always_agent_tool",
@@ -116,13 +116,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:200"
"skill:skills/paperclip/SKILL.md:201"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:status-quick-guide:248",
"id": "skill:skills/paperclip/SKILL.md:status-quick-guide:249",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:248",
"sourceAnchor": "skills/paperclip/SKILL.md:249",
"title": "Status Quick Guide",
"expectedSemantics": "Skill guidance headed “Status Quick Guide”.",
"primaryDisposition": "control_plane_owned",
@@ -131,13 +131,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:248"
"skill:skills/paperclip/SKILL.md:249"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:258",
"id": "skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:259",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:258",
"sourceAnchor": "skills/paperclip/SKILL.md:259",
"title": "Monitors and Watchers (say only what you actually scheduled)",
"expectedSemantics": "Skill guidance headed “Monitors and Watchers (say only what you actually scheduled)”.",
"primaryDisposition": "optional_agent_tool",
@@ -146,13 +146,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:258"
"skill:skills/paperclip/SKILL.md:259"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:delegating-review-tasks:271",
"id": "skill:skills/paperclip/SKILL.md:delegating-review-tasks:272",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:271",
"sourceAnchor": "skills/paperclip/SKILL.md:272",
"title": "Delegating review tasks",
"expectedSemantics": "Skill guidance headed “Delegating review tasks”.",
"primaryDisposition": "always_agent_tool",
@@ -161,13 +161,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:271"
"skill:skills/paperclip/SKILL.md:272"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:282",
"id": "skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:283",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:282",
"sourceAnchor": "skills/paperclip/SKILL.md:283",
"title": "Managing A User's Inbox",
"expectedSemantics": "Skill guidance headed “Managing A User's Inbox”.",
"primaryDisposition": "control_plane_owned",
@@ -176,13 +176,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:282"
"skill:skills/paperclip/SKILL.md:283"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:290",
"id": "skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:291",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:290",
"sourceAnchor": "skills/paperclip/SKILL.md:291",
"title": "Issue Dependencies (Blockers)",
"expectedSemantics": "Skill guidance headed “Issue Dependencies (Blockers)”.",
"primaryDisposition": "control_plane_owned",
@@ -191,13 +191,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:290"
"skill:skills/paperclip/SKILL.md:291"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:requesting-board-approval:315",
"id": "skill:skills/paperclip/SKILL.md:requesting-board-approval:316",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:315",
"sourceAnchor": "skills/paperclip/SKILL.md:316",
"title": "Requesting Board Approval",
"expectedSemantics": "Skill guidance headed “Requesting Board Approval”.",
"primaryDisposition": "optional_agent_tool",
@@ -206,13 +206,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:315"
"skill:skills/paperclip/SKILL.md:316"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:issue-thread-interactions:336",
"id": "skill:skills/paperclip/SKILL.md:issue-thread-interactions:337",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:336",
"sourceAnchor": "skills/paperclip/SKILL.md:337",
"title": "Issue-Thread Interactions",
"expectedSemantics": "Skill guidance headed “Issue-Thread Interactions”.",
"primaryDisposition": "optional_agent_tool",
@@ -221,13 +221,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:336"
"skill:skills/paperclip/SKILL.md:337"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:standalone-decisions:366",
"id": "skill:skills/paperclip/SKILL.md:standalone-decisions:367",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:366",
"sourceAnchor": "skills/paperclip/SKILL.md:367",
"title": "Standalone Decisions",
"expectedSemantics": "Skill guidance headed “Standalone Decisions”.",
"primaryDisposition": "optional_agent_tool",
@@ -236,13 +236,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:366"
"skill:skills/paperclip/SKILL.md:367"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:470",
"id": "skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:471",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:470",
"sourceAnchor": "skills/paperclip/SKILL.md:471",
"title": "MCP Tool Approval Gates",
"expectedSemantics": "Skill guidance headed “MCP Tool Approval Gates”.",
"primaryDisposition": "optional_agent_tool",
@@ -251,13 +251,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:470"
"skill:skills/paperclip/SKILL.md:471"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:niche-workflow-pointers:512",
"id": "skill:skills/paperclip/SKILL.md:niche-workflow-pointers:513",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:512",
"sourceAnchor": "skills/paperclip/SKILL.md:513",
"title": "Niche Workflow Pointers",
"expectedSemantics": "Skill guidance headed “Niche Workflow Pointers”.",
"primaryDisposition": "optional_agent_tool",
@@ -266,13 +266,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:512"
"skill:skills/paperclip/SKILL.md:513"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:cases:522",
"id": "skill:skills/paperclip/SKILL.md:cases:523",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:522",
"sourceAnchor": "skills/paperclip/SKILL.md:523",
"title": "Cases",
"expectedSemantics": "Skill guidance headed “Cases”.",
"primaryDisposition": "optional_agent_tool",
@@ -281,13 +281,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:522"
"skill:skills/paperclip/SKILL.md:523"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:company-skills-workflow:527",
"id": "skill:skills/paperclip/SKILL.md:company-skills-workflow:528",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:527",
"sourceAnchor": "skills/paperclip/SKILL.md:528",
"title": "Company Skills Workflow",
"expectedSemantics": "Skill guidance headed “Company Skills Workflow”.",
"primaryDisposition": "optional_agent_tool",
@@ -296,13 +296,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:527"
"skill:skills/paperclip/SKILL.md:528"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:routines:538",
"id": "skill:skills/paperclip/SKILL.md:routines:539",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:538",
"sourceAnchor": "skills/paperclip/SKILL.md:539",
"title": "Routines",
"expectedSemantics": "Skill guidance headed “Routines”.",
"primaryDisposition": "optional_agent_tool",
@@ -311,13 +311,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:538"
"skill:skills/paperclip/SKILL.md:539"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:task-files-and-workspace-choices:549",
"id": "skill:skills/paperclip/SKILL.md:task-files-and-workspace-choices:550",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:549",
"sourceAnchor": "skills/paperclip/SKILL.md:550",
"title": "Task Files and Workspace Choices",
"expectedSemantics": "Skill guidance headed “Task Files and Workspace Choices”.",
"primaryDisposition": "optional_agent_tool",
@@ -326,13 +326,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:549"
"skill:skills/paperclip/SKILL.md:550"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:555",
"id": "skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:558",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:555",
"sourceAnchor": "skills/paperclip/SKILL.md:558",
"title": "Issue Workspace Runtime Controls",
"expectedSemantics": "Skill guidance headed “Issue Workspace Runtime Controls”.",
"primaryDisposition": "optional_agent_tool",
@@ -341,13 +341,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:555"
"skill:skills/paperclip/SKILL.md:558"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:proposing-credentials-safely:562",
"id": "skill:skills/paperclip/SKILL.md:proposing-credentials-safely:565",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:562",
"sourceAnchor": "skills/paperclip/SKILL.md:565",
"title": "Proposing Credentials Safely",
"expectedSemantics": "Skill guidance headed “Proposing Credentials Safely”.",
"primaryDisposition": "optional_agent_tool",
@@ -356,13 +356,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:562"
"skill:skills/paperclip/SKILL.md:565"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:reading-granted-secrets:569",
"id": "skill:skills/paperclip/SKILL.md:reading-granted-secrets:572",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:569",
"sourceAnchor": "skills/paperclip/SKILL.md:572",
"title": "Reading Granted Secrets",
"expectedSemantics": "Skill guidance headed “Reading Granted Secrets”.",
"primaryDisposition": "optional_agent_tool",
@@ -371,13 +371,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:569"
"skill:skills/paperclip/SKILL.md:572"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:critical-rules:595",
"id": "skill:skills/paperclip/SKILL.md:critical-rules:598",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:595",
"sourceAnchor": "skills/paperclip/SKILL.md:598",
"title": "Critical Rules",
"expectedSemantics": "Skill guidance headed “Critical Rules”.",
"primaryDisposition": "optional_agent_tool",
@@ -386,13 +386,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:595"
"skill:skills/paperclip/SKILL.md:598"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:comment-style-required:616",
"id": "skill:skills/paperclip/SKILL.md:comment-style-required:619",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:616",
"sourceAnchor": "skills/paperclip/SKILL.md:619",
"title": "Comment Style (Required)",
"expectedSemantics": "Skill guidance headed “Comment Style (Required)”.",
"primaryDisposition": "always_agent_tool",
@@ -401,13 +401,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:616"
"skill:skills/paperclip/SKILL.md:619"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:update:648",
"id": "skill:skills/paperclip/SKILL.md:update:651",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:648",
"sourceAnchor": "skills/paperclip/SKILL.md:651",
"title": "Update",
"expectedSemantics": "Skill guidance headed “Update”.",
"primaryDisposition": "optional_agent_tool",
@@ -416,13 +416,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:648"
"skill:skills/paperclip/SKILL.md:651"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:task-documents-and-deliverables:658",
"id": "skill:skills/paperclip/SKILL.md:task-documents-and-deliverables:661",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:658",
"sourceAnchor": "skills/paperclip/SKILL.md:661",
"title": "Task Documents and Deliverables",
"expectedSemantics": "Skill guidance headed “Task Documents and Deliverables”.",
"primaryDisposition": "always_agent_tool",
@@ -431,13 +431,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:658"
"skill:skills/paperclip/SKILL.md:661"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:672",
"id": "skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:675",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:672",
"sourceAnchor": "skills/paperclip/SKILL.md:675",
"title": "Planning (Required when planning requested)",
"expectedSemantics": "Skill guidance headed “Planning (Required when planning requested)”.",
"primaryDisposition": "optional_agent_tool",
@@ -446,13 +446,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:672"
"skill:skills/paperclip/SKILL.md:675"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:703",
"id": "skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:706",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:703",
"sourceAnchor": "skills/paperclip/SKILL.md:706",
"title": "Key Endpoints (Hot Routes)",
"expectedSemantics": "Skill guidance headed “Key Endpoints (Hot Routes)”.",
"primaryDisposition": "optional_agent_tool",
@@ -461,13 +461,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:703"
"skill:skills/paperclip/SKILL.md:706"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:searching-issues:733",
"id": "skill:skills/paperclip/SKILL.md:searching-issues:736",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:733",
"sourceAnchor": "skills/paperclip/SKILL.md:736",
"title": "Searching Issues",
"expectedSemantics": "Skill guidance headed “Searching Issues”.",
"primaryDisposition": "optional_agent_tool",
@@ -476,13 +476,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:733"
"skill:skills/paperclip/SKILL.md:736"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:full-reference:743",
"id": "skill:skills/paperclip/SKILL.md:full-reference:746",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:743",
"sourceAnchor": "skills/paperclip/SKILL.md:746",
"title": "Full Reference",
"expectedSemantics": "Skill guidance headed “Full Reference”.",
"primaryDisposition": "optional_agent_tool",
@@ -491,13 +491,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:743"
"skill:skills/paperclip/SKILL.md:746"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:conversational-confirmation-answers:747",
"id": "skill:skills/paperclip/SKILL.md:conversational-confirmation-answers:750",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:747",
"sourceAnchor": "skills/paperclip/SKILL.md:750",
"title": "Conversational confirmation answers",
"expectedSemantics": "Skill guidance headed “Conversational confirmation answers”.",
"primaryDisposition": "always_agent_tool",
@@ -506,13 +506,13 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:747"
"skill:skills/paperclip/SKILL.md:750"
]
},
{
"id": "skill:skills/paperclip/SKILL.md:incidental-feedback:764",
"id": "skill:skills/paperclip/SKILL.md:incidental-feedback:767",
"sourceKind": "skill_heading",
"sourceAnchor": "skills/paperclip/SKILL.md:764",
"sourceAnchor": "skills/paperclip/SKILL.md:767",
"title": "Incidental feedback",
"expectedSemantics": "Skill guidance headed “Incidental feedback”.",
"primaryDisposition": "optional_agent_tool",
@@ -521,7 +521,7 @@
"control_plane_invariant"
],
"evidenceIds": [
"skill:skills/paperclip/SKILL.md:764"
"skill:skills/paperclip/SKILL.md:767"
]
},
{
@@ -0,0 +1,72 @@
import type { Db } from "@paperclipai/db";
import { heartbeatRuns } from "@paperclipai/db";
import { eq } from "drizzle-orm";
import { vi, type Mock, type Mocked } from "vitest";
import { createRunDispatch } from "../../modules/run-dispatch/index.js";
import type { createWakeQueue } from "../../modules/wake-queue/index.js";
import { createHeartbeatRunState } from "../../services/heartbeat/run-state.js";
import { createHeartbeatRunPreparation } from "../../services/heartbeat/run-preparation.js";
import type { HeartbeatQueueDependencies } from "../../services/heartbeat/queue.js";
type Run = typeof heartbeatRuns.$inferSelect;
type MockedCallback = "getAgent" | "setRunStatus" | "setWakeupStatus" | "appendRunEvent" | "finalizeAgentStatus" | "getAgentInvokability" | "getWorktreeExecutionCutoff" | "applyRunDispatchPostCommitEffects" | "getSchedulingSuppression" | "canStartNewExternalBotWork" | "executeRun" | "applyWakeQueuePostCommitEffects" | "releaseIssueExecutionAndPromote" | "publishRunLifecyclePluginEvent" | "cancelRunInternal" | "cancelActiveForAgentInternal" | "resumeExecutionWaitComments" | "resumeQueuedCommentInterrupt" | "resumeSavedLegacyComments" | "sweepPendingCleanupLeases";
type QueueTestDependencies = HeartbeatQueueDependencies & { [K in MockedCallback]: Mock<HeartbeatQueueDependencies[K]> } & {
issuesSvc: Mocked<HeartbeatQueueDependencies["issuesSvc"]>;
treeControlSvc: Mocked<HeartbeatQueueDependencies["treeControlSvc"]>;
budgets: Mocked<HeartbeatQueueDependencies["budgets"]>;
instanceSettings: Mocked<HeartbeatQueueDependencies["instanceSettings"]>;
wakeQueue: Mocked<HeartbeatQueueDependencies["wakeQueue"]>;
liveRunExecutions: Mocked<HeartbeatQueueDependencies["liveRunExecutions"]>;
};
export function createHeartbeatQueueTestDependencies(db: Db): QueueTestDependencies {
const state = createHeartbeatRunState(db);
const preparation = createHeartbeatRunPreparation(db);
return {
...preparation,
...state,
getAgent: vi.fn<HeartbeatQueueDependencies["getAgent"]>(async () => { throw new Error("Unexpected agent lookup"); }),
options: {},
setRunStatus: vi.fn<HeartbeatQueueDependencies["setRunStatus"]>(async (id, status, patch) =>
db.update(heartbeatRuns).set({ ...patch, status }).where(eq(heartbeatRuns.id, id)).returning().then(rows => rows[0] ?? null)),
setWakeupStatus: vi.fn<HeartbeatQueueDependencies["setWakeupStatus"]>(async () => undefined),
appendRunEvent: vi.fn<HeartbeatQueueDependencies["appendRunEvent"]>(async () => undefined),
finalizeAgentStatus: vi.fn<HeartbeatQueueDependencies["finalizeAgentStatus"]>(async () => undefined),
getAgentInvokability: vi.fn<HeartbeatQueueDependencies["getAgentInvokability"]>(async () => ({ invokable: true })),
getWorktreeExecutionCutoff: vi.fn<HeartbeatQueueDependencies["getWorktreeExecutionCutoff"]>(async () => null),
applyRunDispatchPostCommitEffects: vi.fn<HeartbeatQueueDependencies["applyRunDispatchPostCommitEffects"]>(),
issuesSvc: {
getDependencyReadiness: vi.fn<HeartbeatQueueDependencies["issuesSvc"]["getDependencyReadiness"]>(),
listDependencyReadiness: vi.fn(async () => new Map()),
},
treeControlSvc: { getActivePauseHoldGate: vi.fn(async () => null) },
budgets: { getInvocationBlock: vi.fn(async () => null) },
instanceSettings: { getExperimental: vi.fn<HeartbeatQueueDependencies["instanceSettings"]["getExperimental"]>() },
runDispatch: createRunDispatch(db),
wakeQueue: {
createAdmissionTransactionScope: vi.fn<ReturnType<typeof createWakeQueue>["createAdmissionTransactionScope"]>(),
admitWakeBehindIssueExecution: vi.fn<ReturnType<typeof createWakeQueue>["admitWakeBehindIssueExecution"]>(),
},
activeRunExecutions: new Set<string>(),
activeRunExecutionPromises: new Set<Promise<void>>(),
activeWakeupPromises: new Set<Promise<unknown>>(),
liveRunExecutions: { has: vi.fn(() => false) },
isHeartbeatRunTerminalStatus: (status: string | null | undefined) => ["succeeded", "failed", "cancelled", "timed_out"].includes(status ?? ""),
isSameTaskScope: (left: string | null, right: string | null) => left === right,
runTaskKey: (run: Run) => typeof run.contextSnapshot?.issueId === "string" ? run.contextSnapshot.issueId : null,
filterZombieCoalesceTarget: <T extends { id: string; status: string }>(run: T | null) => run,
getSchedulingSuppression: vi.fn<HeartbeatQueueDependencies["getSchedulingSuppression"]>(async () => ({ suppressed: false, reason: null } as Awaited<ReturnType<HeartbeatQueueDependencies["getSchedulingSuppression"]>>)),
canStartNewExternalBotWork: vi.fn<HeartbeatQueueDependencies["canStartNewExternalBotWork"]>(async () => true),
executeRun: vi.fn<HeartbeatQueueDependencies["executeRun"]>(async () => undefined),
applyWakeQueuePostCommitEffects: vi.fn<HeartbeatQueueDependencies["applyWakeQueuePostCommitEffects"]>(async () => undefined),
releaseIssueExecutionAndPromote: vi.fn<HeartbeatQueueDependencies["releaseIssueExecutionAndPromote"]>(async () => undefined),
publishRunLifecyclePluginEvent: vi.fn<HeartbeatQueueDependencies["publishRunLifecyclePluginEvent"]>(),
cancelRunInternal: vi.fn<HeartbeatQueueDependencies["cancelRunInternal"]>(async () => undefined),
cancelActiveForAgentInternal: vi.fn<HeartbeatQueueDependencies["cancelActiveForAgentInternal"]>(async () => undefined),
resumeExecutionWaitComments: vi.fn<HeartbeatQueueDependencies["resumeExecutionWaitComments"]>(async () => undefined),
resumeQueuedCommentInterrupt: vi.fn<HeartbeatQueueDependencies["resumeQueuedCommentInterrupt"]>(async () => undefined),
resumeSavedLegacyComments: vi.fn<HeartbeatQueueDependencies["resumeSavedLegacyComments"]>(async () => undefined),
formatIssueIdentifierLink: (_identifier: string | null, fallback: string) => fallback,
sweepPendingCleanupLeases: vi.fn<HeartbeatQueueDependencies["sweepPendingCleanupLeases"]>(),
} satisfies HeartbeatQueueDependencies;
}
@@ -3,7 +3,7 @@ import { and, eq } from "drizzle-orm";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import {
agents, agentWakeupRequests, authUsers, chatConversations, chatEndpoints, chatMessageLinks,
chatPublications, companies, completionContracts, createDb, heartbeatRuns, issueComments, issues, nativeRunFinalizations, nativeRunResults,
chatPublications, companies, companyMemberships, completionContracts, createDb, heartbeatRuns, issueComments, issues, nativeRunFinalizations, nativeRunResults,
statusDecisions, statusDecisionEffects, toolApplications, toolConnections,
} from "@paperclipai/db";
import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "../__tests__/helpers/embedded-postgres.js";
@@ -12,6 +12,9 @@ import { CHAT_RUN_PRESENTATION_AUTHORIZATION_REASON } from "./heartbeat-run-summ
import { resolveChatOriginPublicationBindings } from "./issues.js";
import { NativeRunCoordinatorStore } from "./native-runtime/native-run-coordinator-store.js";
import { finalizeNativeRun, repairCommittedNativeChatResponse } from "./native-runtime/native-run-finalizer.js";
import { createHeartbeatQueueTestDependencies } from "../__tests__/helpers/heartbeat-queue.js";
import { createHeartbeatQueue } from "./heartbeat/queue.js";
import { instanceSettingsService } from "./instance-settings.js";
const support = await getEmbeddedPostgresTestSupport();
if (!support.supported) console.warn(`Monitor publication database unavailable: ${support.reason}`);
@@ -27,8 +30,9 @@ if (!support.supported) console.warn(`Monitor publication database unavailable:
async function fixture() {
const [company] = await db.insert(companies).values({ name: "Continuation", issuePrefix: randomUUID().slice(0, 8) }).returning();
await db.insert(companyMemberships).values({ companyId: company.id, principalType: "user", principalId: "requester", status: "active", membershipRole: "owner" });
const [agent] = await db.insert(agents).values({ companyId: company.id, name: "Worker", adapterType: "paperclip_runner" }).returning();
const [issue] = await db.insert(issues).values({ companyId: company.id, title: "Repository QA", status: "in_progress", assigneeAgentId: agent.id }).returning();
const [issue] = await db.insert(issues).values({ companyId: company.id, title: "Repository QA", status: "in_progress", assigneeAgentId: agent.id, responsibleUserId: "requester" }).returning();
const [application] = await db.insert(toolApplications).values({ companyId: company.id, applicationKey: randomUUID(), name: "Slack", type: "chat" }).returning();
const [connection] = await db.insert(toolConnections).values({ companyId: company.id, applicationId: application.id, name: "Slack", uid: randomUUID(), connectionPurpose: "channel", transport: "chat_sdk" }).returning();
const [endpoint] = await db.insert(chatEndpoints).values({ companyId: company.id, connectionId: connection.id, provider: "slack", publicId: randomUUID(), assignedAgentId: agent.id, status: "active" }).returning();
@@ -161,11 +165,21 @@ if (!support.supported) console.warn(`Monitor publication database unavailable:
await finalizeNativeRun({ db, runId: f.source.id, workspaceFinalizeStatus: "succeeded", projectRunStatus: true });
const [wake] = await db.select().from(agentWakeupRequests).where(and(eq(agentWakeupRequests.companyId, f.company.id), eq(agentWakeupRequests.reason, "issue_status_changed")));
expect(wake).toBeDefined();
await db.update(agentWakeupRequests).set({ status: "claimed", runId: f.successor.id }).where(eq(agentWakeupRequests.id, wake.id));
await db.update(heartbeatRuns).set({ status: "running", finishedAt: null,
contextSnapshot: { source: "native_status_decision", issueId: f.issue.id },
}).where(eq(heartbeatRuns.id, f.successor.id));
return { ...f, continuationWake: wake, summary };
const deps = createHeartbeatQueueTestDependencies(db);
deps.getAgent.mockImplementation(async id => (await db.select().from(agents).where(eq(agents.id, id)))[0] ?? null);
deps.instanceSettings.getExperimental.mockImplementation(() => instanceSettingsService(db).getExperimental());
await db.update(agents).set({ runtimeConfig: { heartbeat: { wakeOnDemand: true } } }).where(eq(agents.id, f.agent.id));
const queue = createHeartbeatQueue(db, deps);
expect(await queue.dispatchPendingNativeStatusWakeups({ companyId: f.company.id })).toMatchObject({ dispatched: 1 });
await queue.startNextQueuedRunForAgent(f.agent.id);
await Promise.all([...deps.activeRunExecutionPromises]);
const [delivered] = await db.select().from(agentWakeupRequests).where(eq(agentWakeupRequests.id, wake.id));
expect(delivered).toMatchObject({ status: "coalesced" });
expect(delivered.runId).toBeTruthy();
const [successor] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, delivered.runId!));
expect(successor.responsibleUserId).toBe("requester");
return { ...f, successor, continuationWake: delivered, summary,
bindings: (runId = successor.id) => resolveChatOriginPublicationBindings(db, f.company.id, f.issue.id, runId) };
}
it("delivers the saved setup explanation and retains chat through the committed automatic continuation", async () => {
@@ -182,11 +196,13 @@ if (!support.supported) console.warn(`Monitor publication database unavailable:
expect(await db.select().from(nativeRunResults).where(eq(nativeRunResults.runId, f.source.id))).toHaveLength(1);
});
it.each(["effect", "decision", "wake", "requester", "agent"])("rejects automatic continuation when %s authority is no longer valid", async fault => {
it.each(["effect", "decision", "wake", "dispatch", "requester", "agent"])("rejects automatic continuation when %s authority is no longer valid", async fault => {
const f = await continuedTaskFixture();
if (fault === "effect") await db.update(statusDecisionEffects).set({ targetId: randomUUID() }).where(eq(statusDecisionEffects.companyId, f.company.id));
if (fault === "decision") await db.update(statusDecisions).set({ applicationState: "proposed" }).where(eq(statusDecisions.companyId, f.company.id));
if (fault === "wake") await db.update(agentWakeupRequests).set({ idempotencyKey: randomUUID() }).where(eq(agentWakeupRequests.id, f.continuationWake.id));
if (fault === "dispatch") await db.update(agentWakeupRequests).set({ requestedByActorId: "unrelated-dispatch" }).where(and(
eq(agentWakeupRequests.companyId, f.company.id), eq(agentWakeupRequests.requestedByActorId, `native-status-wake-dispatch:${f.continuationWake.id}`)));
if (fault === "requester") await db.update(heartbeatRuns).set({ responsibleUserId: "another-person" }).where(eq(heartbeatRuns.id, f.successor.id));
if (fault === "agent") {
const [other] = await db.insert(agents).values({ companyId: f.company.id, name: "Other", adapterType: "paperclip_runner" }).returning();
+1 -1
View File
@@ -757,7 +757,7 @@ export async function enqueueChatRunMilestones(
and continuation_wake.run_id = ${heartbeatRuns.id}
and continuation_wake.agent_id = ${heartbeatRuns.agentId}
and continuation_wake.reason = 'issue_status_changed'
and continuation_wake.status in ('claimed', 'completed')
and continuation_wake.status in ('queued', 'claimed', 'completed', 'coalesced')
and continuation_wake.payload->>'issueId' = ${issueIdFromContext}
)
)`;
+2 -56
View File
@@ -4,66 +4,12 @@ import { agents, agentWakeupRequests, caseHandoffs, cases, companies, createDb,
import { eq, sql } from "drizzle-orm";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "../../__tests__/helpers/embedded-postgres.js";
import { createRunDispatch } from "../../modules/run-dispatch/index.js";
import type { createWakeQueue } from "../../modules/wake-queue/index.js";
import { createPostgresWakeQueueAdapter } from "../../modules/wake-queue/adapters/postgres.js";
import { createReleaseIssueExecution } from "../../modules/wake-queue/application/use-cases.js";
import { createHeartbeatRunState } from "./run-state.js";
import { createHeartbeatRunPreparation } from "./run-preparation.js";
import { createHeartbeatQueue, type HeartbeatQueueDependencies } from "./queue.js";
type Run = typeof heartbeatRuns.$inferSelect;
function callbacks(db: Db) {
const state = createHeartbeatRunState(db);
const preparation = createHeartbeatRunPreparation(db);
return {
...preparation,
...state,
getAgent: vi.fn<HeartbeatQueueDependencies["getAgent"]>(async () => { throw new Error("Unexpected agent lookup"); }),
options: {},
setRunStatus: vi.fn<HeartbeatQueueDependencies["setRunStatus"]>(async (id, status, patch) =>
db.update(heartbeatRuns).set({ ...patch, status }).where(eq(heartbeatRuns.id, id)).returning().then(rows => rows[0] ?? null)),
setWakeupStatus: vi.fn<HeartbeatQueueDependencies["setWakeupStatus"]>(async () => undefined),
appendRunEvent: vi.fn<HeartbeatQueueDependencies["appendRunEvent"]>(async () => undefined),
finalizeAgentStatus: vi.fn(async () => undefined),
getAgentInvokability: vi.fn<HeartbeatQueueDependencies["getAgentInvokability"]>(async () => ({ invokable: true })),
getWorktreeExecutionCutoff: vi.fn(async () => null),
applyRunDispatchPostCommitEffects: vi.fn(),
issuesSvc: {
getDependencyReadiness: vi.fn<HeartbeatQueueDependencies["issuesSvc"]["getDependencyReadiness"]>(),
listDependencyReadiness: vi.fn(async () => new Map()),
},
treeControlSvc: { getActivePauseHoldGate: vi.fn(async () => null) },
budgets: { getInvocationBlock: vi.fn(async () => null) },
instanceSettings: { getExperimental: vi.fn<HeartbeatQueueDependencies["instanceSettings"]["getExperimental"]>() },
runDispatch: createRunDispatch(db),
wakeQueue: {
createAdmissionTransactionScope: vi.fn<ReturnType<typeof createWakeQueue>["createAdmissionTransactionScope"]>(),
admitWakeBehindIssueExecution: vi.fn<ReturnType<typeof createWakeQueue>["admitWakeBehindIssueExecution"]>(),
},
activeRunExecutions: new Set<string>(),
activeRunExecutionPromises: new Set<Promise<void>>(),
activeWakeupPromises: new Set<Promise<unknown>>(),
liveRunExecutions: { has: vi.fn(() => false) },
isHeartbeatRunTerminalStatus: (status: string | null | undefined) => ["succeeded", "failed", "cancelled", "timed_out"].includes(status ?? ""),
isSameTaskScope: (left: string | null, right: string | null) => left === right,
runTaskKey: (run: Run) => typeof run.contextSnapshot?.issueId === "string" ? run.contextSnapshot.issueId : null,
filterZombieCoalesceTarget: <T extends { id: string; status: string }>(run: T | null) => run,
getSchedulingSuppression: vi.fn(async () => ({ suppressed: false, reason: null } as Awaited<ReturnType<HeartbeatQueueDependencies["getSchedulingSuppression"]>>)),
canStartNewExternalBotWork: vi.fn(async () => true),
executeRun: vi.fn(async () => undefined),
applyWakeQueuePostCommitEffects: vi.fn(async () => undefined),
releaseIssueExecutionAndPromote: vi.fn<HeartbeatQueueDependencies["releaseIssueExecutionAndPromote"]>(async () => undefined),
publishRunLifecyclePluginEvent: vi.fn(),
cancelRunInternal: vi.fn(async () => undefined),
cancelActiveForAgentInternal: vi.fn(async () => undefined),
resumeExecutionWaitComments: vi.fn(async () => undefined),
resumeQueuedCommentInterrupt: vi.fn(async () => undefined),
resumeSavedLegacyComments: vi.fn(async () => undefined),
formatIssueIdentifierLink: (_identifier: string | null, fallback: string) => fallback,
sweepPendingCleanupLeases: vi.fn<HeartbeatQueueDependencies["sweepPendingCleanupLeases"]>(),
} satisfies HeartbeatQueueDependencies;
}
import { createHeartbeatQueueTestDependencies as callbacks } from "../../__tests__/helpers/heartbeat-queue.js";
function guardedDatabase() {
const access = vi.fn(() => { throw new Error("Unexpected queue database access"); });
return { db: new Proxy({}, { get: access }) as Db, access };
@@ -10,14 +10,27 @@ export async function readNativeContinuationSource(
binding: { companyId: string; issueId: string; runId: string },
): Promise<string | null> {
const source = alias(heartbeatRuns, "continuation_source");
const dispatch = alias(agentWakeupRequests, "continuation_dispatch");
const sources = await db.select({ runId: source.id }).from(heartbeatRuns)
.innerJoin(agentWakeupRequests, and(
eq(agentWakeupRequests.companyId, binding.companyId),
eq(agentWakeupRequests.runId, heartbeatRuns.id),
eq(agentWakeupRequests.agentId, heartbeatRuns.agentId),
eq(agentWakeupRequests.reason, "issue_status_changed"),
inArray(agentWakeupRequests.status, ["claimed", "completed"]),
eq(agentWakeupRequests.status, "coalesced"),
eq(agentWakeupRequests.requestedByActorType, "system"),
eq(agentWakeupRequests.requestedByActorId, "native-status-committer"),
sql`${agentWakeupRequests.payload}->>'issueId' = ${binding.issueId}`,
sql`${heartbeatRuns.contextSnapshot}->>'nativeStatusWakeIntentId' = ${agentWakeupRequests.id}::text`,
))
.innerJoin(dispatch, and(
eq(dispatch.companyId, binding.companyId), eq(dispatch.agentId, heartbeatRuns.agentId),
eq(dispatch.runId, heartbeatRuns.id), eq(dispatch.reason, "issue_status_changed"),
eq(dispatch.requestedByActorType, "system"),
sql`${dispatch.requestedByActorId} = 'native-status-wake-dispatch:' || ${agentWakeupRequests.id}::text`,
eq(dispatch.idempotencyKey, agentWakeupRequests.idempotencyKey),
inArray(dispatch.status, ["queued", "claimed", "completed", "coalesced"]),
sql`${dispatch.payload}->>'issueId' = ${binding.issueId}`,
))
.innerJoin(statusDecisionEffects, and(
eq(statusDecisionEffects.companyId, binding.companyId), eq(statusDecisionEffects.issueId, binding.issueId),