diff --git a/packages/paperclip-runner/docs/capability-contract.md b/packages/paperclip-runner/docs/capability-contract.md index 0b73d59fbb..ac5c082c95 100644 --- a/packages/paperclip-runner/docs/capability-contract.md +++ b/packages/paperclip-runner/docs/capability-contract.md @@ -8,9 +8,9 @@ The skill/reference inventory and eval cases are the only normative behavior sou ## Baseline Counts -- Skill/reference headings: 152 +- Skill/reference headings: 153 - Eval cases: 106 across 16 groups -- Total normative rows: 258 +- Total normative rows: 259 - Legacy MCP aliases folded into normative rows: 42 | Eval group | Cases | @@ -44,31 +44,32 @@ The skill/reference inventory and eval cases are the only normative behavior sou | skill:skills/paperclip/SKILL.md:paperclip-skill:10 | optional_agent_tool | skills/paperclip/SKILL.md:10 | | skill:skills/paperclip/SKILL.md:terminology:14 | optional_agent_tool | skills/paperclip/SKILL.md:14 | | skill:skills/paperclip/SKILL.md:authentication:18 | control_plane_owned | skills/paperclip/SKILL.md:18 | -| skill:skills/paperclip/SKILL.md:the-heartbeat-procedure:30 | optional_agent_tool | skills/paperclip/SKILL.md:30 | -| skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:101 | always_agent_tool | skills/paperclip/SKILL.md:101 | -| skill:skills/paperclip/SKILL.md:status-quick-guide:148 | control_plane_owned | skills/paperclip/SKILL.md:148 | -| skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:158 | optional_agent_tool | skills/paperclip/SKILL.md:158 | -| skill:skills/paperclip/SKILL.md:delegating-review-tasks:171 | always_agent_tool | skills/paperclip/SKILL.md:171 | -| skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:182 | control_plane_owned | skills/paperclip/SKILL.md:182 | -| skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:190 | control_plane_owned | skills/paperclip/SKILL.md:190 | -| skill:skills/paperclip/SKILL.md:requesting-board-approval:215 | optional_agent_tool | skills/paperclip/SKILL.md:215 | -| skill:skills/paperclip/SKILL.md:issue-thread-interactions:236 | optional_agent_tool | skills/paperclip/SKILL.md:236 | -| skill:skills/paperclip/SKILL.md:standalone-decisions:265 | optional_agent_tool | skills/paperclip/SKILL.md:265 | -| skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:369 | optional_agent_tool | skills/paperclip/SKILL.md:369 | -| skill:skills/paperclip/SKILL.md:niche-workflow-pointers:411 | optional_agent_tool | skills/paperclip/SKILL.md:411 | -| skill:skills/paperclip/SKILL.md:cases:421 | optional_agent_tool | skills/paperclip/SKILL.md:421 | -| skill:skills/paperclip/SKILL.md:company-skills-workflow:426 | optional_agent_tool | skills/paperclip/SKILL.md:426 | -| skill:skills/paperclip/SKILL.md:routines:437 | optional_agent_tool | skills/paperclip/SKILL.md:437 | -| skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:448 | optional_agent_tool | skills/paperclip/SKILL.md:448 | -| skill:skills/paperclip/SKILL.md:proposing-credentials-safely:455 | optional_agent_tool | skills/paperclip/SKILL.md:455 | -| skill:skills/paperclip/SKILL.md:reading-granted-secrets:462 | optional_agent_tool | skills/paperclip/SKILL.md:462 | -| skill:skills/paperclip/SKILL.md:critical-rules:488 | optional_agent_tool | skills/paperclip/SKILL.md:488 | -| skill:skills/paperclip/SKILL.md:comment-style-required:512 | always_agent_tool | skills/paperclip/SKILL.md:512 | -| skill:skills/paperclip/SKILL.md:update:544 | optional_agent_tool | skills/paperclip/SKILL.md:544 | -| skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:554 | optional_agent_tool | skills/paperclip/SKILL.md:554 | -| skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:587 | optional_agent_tool | skills/paperclip/SKILL.md:587 | -| skill:skills/paperclip/SKILL.md:searching-issues:616 | optional_agent_tool | skills/paperclip/SKILL.md:616 | -| skill:skills/paperclip/SKILL.md:full-reference:626 | optional_agent_tool | skills/paperclip/SKILL.md:626 | +| skill:skills/paperclip/SKILL.md:conversation-tasks:30 | optional_agent_tool | skills/paperclip/SKILL.md:30 | +| skill:skills/paperclip/SKILL.md:the-heartbeat-procedure:47 | optional_agent_tool | skills/paperclip/SKILL.md:47 | +| skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:118 | always_agent_tool | skills/paperclip/SKILL.md:118 | +| skill:skills/paperclip/SKILL.md:status-quick-guide:165 | control_plane_owned | skills/paperclip/SKILL.md:165 | +| skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:175 | optional_agent_tool | skills/paperclip/SKILL.md:175 | +| skill:skills/paperclip/SKILL.md:delegating-review-tasks:188 | always_agent_tool | skills/paperclip/SKILL.md:188 | +| skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:199 | control_plane_owned | skills/paperclip/SKILL.md:199 | +| skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:207 | control_plane_owned | skills/paperclip/SKILL.md:207 | +| skill:skills/paperclip/SKILL.md:requesting-board-approval:232 | optional_agent_tool | skills/paperclip/SKILL.md:232 | +| skill:skills/paperclip/SKILL.md:issue-thread-interactions:253 | optional_agent_tool | skills/paperclip/SKILL.md:253 | +| skill:skills/paperclip/SKILL.md:standalone-decisions:282 | optional_agent_tool | skills/paperclip/SKILL.md:282 | +| skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:386 | optional_agent_tool | skills/paperclip/SKILL.md:386 | +| skill:skills/paperclip/SKILL.md:niche-workflow-pointers:428 | optional_agent_tool | skills/paperclip/SKILL.md:428 | +| skill:skills/paperclip/SKILL.md:cases:438 | optional_agent_tool | skills/paperclip/SKILL.md:438 | +| skill:skills/paperclip/SKILL.md:company-skills-workflow:443 | optional_agent_tool | skills/paperclip/SKILL.md:443 | +| skill:skills/paperclip/SKILL.md:routines:454 | optional_agent_tool | skills/paperclip/SKILL.md:454 | +| skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:465 | optional_agent_tool | skills/paperclip/SKILL.md:465 | +| skill:skills/paperclip/SKILL.md:proposing-credentials-safely:472 | optional_agent_tool | skills/paperclip/SKILL.md:472 | +| skill:skills/paperclip/SKILL.md:reading-granted-secrets:479 | optional_agent_tool | skills/paperclip/SKILL.md:479 | +| skill:skills/paperclip/SKILL.md:critical-rules:505 | optional_agent_tool | skills/paperclip/SKILL.md:505 | +| skill:skills/paperclip/SKILL.md:comment-style-required:529 | always_agent_tool | skills/paperclip/SKILL.md:529 | +| skill:skills/paperclip/SKILL.md:update:561 | optional_agent_tool | skills/paperclip/SKILL.md:561 | +| skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:571 | optional_agent_tool | skills/paperclip/SKILL.md:571 | +| skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:604 | optional_agent_tool | skills/paperclip/SKILL.md:604 | +| skill:skills/paperclip/SKILL.md:searching-issues:633 | optional_agent_tool | skills/paperclip/SKILL.md:633 | +| skill:skills/paperclip/SKILL.md:full-reference:643 | optional_agent_tool | skills/paperclip/SKILL.md:643 | | skill:skills/paperclip/references/artifacts.md:generated-artifacts-and-work-products:1 | always_agent_tool | skills/paperclip/references/artifacts.md:1 | | skill:skills/paperclip/references/artifacts.md:workspace-only-file-references:15 | optional_agent_tool | skills/paperclip/references/artifacts.md:15 | | skill:skills/paperclip/references/cases.md:cases:1 | optional_agent_tool | skills/paperclip/references/cases.md:1 | @@ -171,28 +172,28 @@ The skill/reference inventory and eval cases are the only normative behavior sou | skill:skills/paperclip/references/api-reference.md:openclaw-invite-prompt-ceo:731 | optional_agent_tool | skills/paperclip/references/api-reference.md:731 | | skill:skills/paperclip/references/api-reference.md:setting-agent-instructions-path:750 | optional_agent_tool | skills/paperclip/references/api-reference.md:750 | | skill:skills/paperclip/references/api-reference.md:project-setup-create-workspace:783 | optional_agent_tool | skills/paperclip/references/api-reference.md:783 | -| skill:skills/paperclip/references/api-reference.md:option-a-one-call-create-with-workspace:787 | optional_agent_tool | skills/paperclip/references/api-reference.md:787 | -| skill:skills/paperclip/references/api-reference.md:option-b-two-calls-project-first-then-workspace:806 | optional_agent_tool | skills/paperclip/references/api-reference.md:806 | -| skill:skills/paperclip/references/api-reference.md:governance-and-approvals:835 | optional_agent_tool | skills/paperclip/references/api-reference.md:835 | -| skill:skills/paperclip/references/api-reference.md:requesting-a-hire-management-only:839 | optional_agent_tool | skills/paperclip/references/api-reference.md:839 | -| skill:skills/paperclip/references/api-reference.md:ceo-strategy-approval:859 | optional_agent_tool | skills/paperclip/references/api-reference.md:859 | -| skill:skills/paperclip/references/api-reference.md:issue-thread-confirmations:868 | always_agent_tool | skills/paperclip/references/api-reference.md:868 | -| skill:skills/paperclip/references/api-reference.md:checkbox-confirmations:926 | always_agent_tool | skills/paperclip/references/api-reference.md:926 | -| skill:skills/paperclip/references/api-reference.md:item-verdict-requests:1041 | optional_agent_tool | skills/paperclip/references/api-reference.md:1041 | -| skill:skills/paperclip/references/api-reference.md:checking-approval-status:1151 | optional_agent_tool | skills/paperclip/references/api-reference.md:1151 | -| skill:skills/paperclip/references/api-reference.md:approval-follow-up-requesting-agent:1157 | always_agent_tool | skills/paperclip/references/api-reference.md:1157 | -| skill:skills/paperclip/references/api-reference.md:issue-lifecycle:1175 | always_agent_tool | skills/paperclip/references/api-reference.md:1175 | -| skill:skills/paperclip/references/api-reference.md:error-handling:1205 | control_plane_owned | skills/paperclip/references/api-reference.md:1205 | -| skill:skills/paperclip/references/api-reference.md:full-api-reference:1219 | optional_agent_tool | skills/paperclip/references/api-reference.md:1219 | -| skill:skills/paperclip/references/api-reference.md:agents:1221 | optional_agent_tool | skills/paperclip/references/api-reference.md:1221 | -| skill:skills/paperclip/references/api-reference.md:issues-tasks:1242 | optional_agent_tool | skills/paperclip/references/api-reference.md:1242 | -| skill:skills/paperclip/references/api-reference.md:companies-projects-goals:1282 | optional_agent_tool | skills/paperclip/references/api-reference.md:1282 | -| skill:skills/paperclip/references/api-reference.md:routines:1306 | optional_agent_tool | skills/paperclip/references/api-reference.md:1306 | -| skill:skills/paperclip/references/api-reference.md:approvals-costs-activity-dashboard:1322 | optional_agent_tool | skills/paperclip/references/api-reference.md:1322 | -| skill:skills/paperclip/references/api-reference.md:secrets:1344 | optional_agent_tool | skills/paperclip/references/api-reference.md:1344 | -| skill:skills/paperclip/references/api-reference.md:agent-secret-proposals:1357 | optional_agent_tool | skills/paperclip/references/api-reference.md:1357 | -| skill:skills/paperclip/references/api-reference.md:agent-secret-access:1457 | optional_agent_tool | skills/paperclip/references/api-reference.md:1457 | -| skill:skills/paperclip/references/api-reference.md:common-mistakes:1497 | optional_agent_tool | skills/paperclip/references/api-reference.md:1497 | +| skill:skills/paperclip/references/api-reference.md:option-a-one-call-create-with-workspace:807 | optional_agent_tool | skills/paperclip/references/api-reference.md:807 | +| skill:skills/paperclip/references/api-reference.md:option-b-two-calls-project-first-then-workspace:826 | optional_agent_tool | skills/paperclip/references/api-reference.md:826 | +| skill:skills/paperclip/references/api-reference.md:governance-and-approvals:855 | optional_agent_tool | skills/paperclip/references/api-reference.md:855 | +| skill:skills/paperclip/references/api-reference.md:requesting-a-hire-management-only:859 | optional_agent_tool | skills/paperclip/references/api-reference.md:859 | +| skill:skills/paperclip/references/api-reference.md:ceo-strategy-approval:879 | optional_agent_tool | skills/paperclip/references/api-reference.md:879 | +| skill:skills/paperclip/references/api-reference.md:issue-thread-confirmations:888 | always_agent_tool | skills/paperclip/references/api-reference.md:888 | +| skill:skills/paperclip/references/api-reference.md:checkbox-confirmations:946 | always_agent_tool | skills/paperclip/references/api-reference.md:946 | +| skill:skills/paperclip/references/api-reference.md:item-verdict-requests:1061 | optional_agent_tool | skills/paperclip/references/api-reference.md:1061 | +| skill:skills/paperclip/references/api-reference.md:checking-approval-status:1171 | optional_agent_tool | skills/paperclip/references/api-reference.md:1171 | +| skill:skills/paperclip/references/api-reference.md:approval-follow-up-requesting-agent:1177 | always_agent_tool | skills/paperclip/references/api-reference.md:1177 | +| skill:skills/paperclip/references/api-reference.md:issue-lifecycle:1195 | always_agent_tool | skills/paperclip/references/api-reference.md:1195 | +| skill:skills/paperclip/references/api-reference.md:error-handling:1225 | control_plane_owned | skills/paperclip/references/api-reference.md:1225 | +| skill:skills/paperclip/references/api-reference.md:full-api-reference:1239 | optional_agent_tool | skills/paperclip/references/api-reference.md:1239 | +| skill:skills/paperclip/references/api-reference.md:agents:1241 | optional_agent_tool | skills/paperclip/references/api-reference.md:1241 | +| skill:skills/paperclip/references/api-reference.md:issues-tasks:1262 | optional_agent_tool | skills/paperclip/references/api-reference.md:1262 | +| skill:skills/paperclip/references/api-reference.md:companies-projects-goals:1302 | optional_agent_tool | skills/paperclip/references/api-reference.md:1302 | +| skill:skills/paperclip/references/api-reference.md:routines:1326 | optional_agent_tool | skills/paperclip/references/api-reference.md:1326 | +| skill:skills/paperclip/references/api-reference.md:approvals-costs-activity-dashboard:1342 | optional_agent_tool | skills/paperclip/references/api-reference.md:1342 | +| skill:skills/paperclip/references/api-reference.md:secrets:1364 | optional_agent_tool | skills/paperclip/references/api-reference.md:1364 | +| skill:skills/paperclip/references/api-reference.md:agent-secret-proposals:1377 | optional_agent_tool | skills/paperclip/references/api-reference.md:1377 | +| skill:skills/paperclip/references/api-reference.md:agent-secret-access:1477 | optional_agent_tool | skills/paperclip/references/api-reference.md:1477 | +| skill:skills/paperclip/references/api-reference.md:common-mistakes:1517 | optional_agent_tool | skills/paperclip/references/api-reference.md:1517 | ## Legacy MCP Alias Index diff --git a/packages/paperclip-runner/docs/capability-disposition.md b/packages/paperclip-runner/docs/capability-disposition.md index 62f281797b..7eec1d8b47 100644 --- a/packages/paperclip-runner/docs/capability-disposition.md +++ b/packages/paperclip-runner/docs/capability-disposition.md @@ -12,10 +12,10 @@ the authoritative rows. Only two sources are normative: 1. The Paperclip skill and its seven references (`SKILL.md` plus - `references/*.md`), contributing **152 headings**. + `references/*.md`), contributing **153 headings**. 2. The Paperclip Evals corpus, contributing **106 cases across 16 groups**. -Together these produce **258 normative rows**. The legacy Paperclip MCP tool +Together these produce **259 normative rows**. The legacy Paperclip MCP tool surface (**41 tools**) is not a production capability surface; each MCP name is folded one-to-one into a normative eval row as a traceability alias and inherits that row's disposition. The contract prints the alias index only so the diff --git a/packages/paperclip-runner/generated/capability/capabilities.yaml b/packages/paperclip-runner/generated/capability/capabilities.yaml index 3b67f75f1b..c38d964136 100644 --- a/packages/paperclip-runner/generated/capability/capabilities.yaml +++ b/packages/paperclip-runner/generated/capability/capabilities.yaml @@ -31,223 +31,232 @@ { "id": "skill:skills/paperclip/SKILL.md:30", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L30:the-heartbeat-procedure", + "sourceAnchor": "skills/paperclip/SKILL.md#L30:conversation-tasks", + "heading": "Conversation tasks", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:47", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L47:the-heartbeat-procedure", "heading": "The Heartbeat Procedure", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:101", + "id": "skill:skills/paperclip/SKILL.md:118", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L101:generated-artifacts-and-work-products", + "sourceAnchor": "skills/paperclip/SKILL.md#L118: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:148", + "id": "skill:skills/paperclip/SKILL.md:165", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L148:status-quick-guide", + "sourceAnchor": "skills/paperclip/SKILL.md#L165:status-quick-guide", "heading": "Status Quick Guide", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:158", + "id": "skill:skills/paperclip/SKILL.md:175", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L158:monitors-and-watchers-say-only-what-you-actually-scheduled", + "sourceAnchor": "skills/paperclip/SKILL.md#L175: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:171", + "id": "skill:skills/paperclip/SKILL.md:188", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L171:delegating-review-tasks", + "sourceAnchor": "skills/paperclip/SKILL.md#L188:delegating-review-tasks", "heading": "Delegating review tasks", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:182", + "id": "skill:skills/paperclip/SKILL.md:199", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L182:managing-a-user-s-inbox", + "sourceAnchor": "skills/paperclip/SKILL.md#L199: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:190", + "id": "skill:skills/paperclip/SKILL.md:207", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L190:issue-dependencies-blockers", + "sourceAnchor": "skills/paperclip/SKILL.md#L207:issue-dependencies-blockers", "heading": "Issue Dependencies (Blockers)", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:215", + "id": "skill:skills/paperclip/SKILL.md:232", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L215:requesting-board-approval", + "sourceAnchor": "skills/paperclip/SKILL.md#L232:requesting-board-approval", "heading": "Requesting Board Approval", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:236", + "id": "skill:skills/paperclip/SKILL.md:253", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L236:issue-thread-interactions", + "sourceAnchor": "skills/paperclip/SKILL.md#L253:issue-thread-interactions", "heading": "Issue-Thread Interactions", "primaryDisposition": "always_agent_tool", "semanticOperation": "request_human_input", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:265", + "id": "skill:skills/paperclip/SKILL.md:282", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L265:standalone-decisions", + "sourceAnchor": "skills/paperclip/SKILL.md#L282:standalone-decisions", "heading": "Standalone Decisions", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:369", + "id": "skill:skills/paperclip/SKILL.md:386", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L369:mcp-tool-approval-gates", + "sourceAnchor": "skills/paperclip/SKILL.md#L386: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:411", + "id": "skill:skills/paperclip/SKILL.md:428", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L411:niche-workflow-pointers", + "sourceAnchor": "skills/paperclip/SKILL.md#L428:niche-workflow-pointers", "heading": "Niche Workflow Pointers", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:421", + "id": "skill:skills/paperclip/SKILL.md:438", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L421:cases", + "sourceAnchor": "skills/paperclip/SKILL.md#L438:cases", "heading": "Cases", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:426", + "id": "skill:skills/paperclip/SKILL.md:443", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L426:company-skills-workflow", + "sourceAnchor": "skills/paperclip/SKILL.md#L443:company-skills-workflow", "heading": "Company Skills Workflow", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:437", + "id": "skill:skills/paperclip/SKILL.md:454", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L437:routines", + "sourceAnchor": "skills/paperclip/SKILL.md#L454:routines", "heading": "Routines", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:448", + "id": "skill:skills/paperclip/SKILL.md:465", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L448:issue-workspace-runtime-controls", + "sourceAnchor": "skills/paperclip/SKILL.md#L465: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:455", + "id": "skill:skills/paperclip/SKILL.md:472", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L455:proposing-credentials-safely", + "sourceAnchor": "skills/paperclip/SKILL.md#L472:proposing-credentials-safely", "heading": "Proposing Credentials Safely", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:462", + "id": "skill:skills/paperclip/SKILL.md:479", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L462:reading-granted-secrets", + "sourceAnchor": "skills/paperclip/SKILL.md#L479:reading-granted-secrets", "heading": "Reading Granted Secrets", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:488", + "id": "skill:skills/paperclip/SKILL.md:505", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L488:critical-rules", + "sourceAnchor": "skills/paperclip/SKILL.md#L505:critical-rules", "heading": "Critical Rules", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:512", + "id": "skill:skills/paperclip/SKILL.md:529", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L512:comment-style-required", + "sourceAnchor": "skills/paperclip/SKILL.md#L529:comment-style-required", "heading": "Comment Style (Required)", "primaryDisposition": "always_agent_tool", "semanticOperation": "report_progress", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/SKILL.md:544", + "id": "skill:skills/paperclip/SKILL.md:561", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L544:update", + "sourceAnchor": "skills/paperclip/SKILL.md#L561:update", "heading": "Update", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:554", + "id": "skill:skills/paperclip/SKILL.md:571", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L554:planning-required-when-planning-requested", + "sourceAnchor": "skills/paperclip/SKILL.md#L571: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:587", + "id": "skill:skills/paperclip/SKILL.md:604", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L587:key-endpoints-hot-routes", + "sourceAnchor": "skills/paperclip/SKILL.md#L604: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:616", + "id": "skill:skills/paperclip/SKILL.md:633", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L616:searching-issues", + "sourceAnchor": "skills/paperclip/SKILL.md#L633:searching-issues", "heading": "Searching Issues", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/SKILL.md:626", + "id": "skill:skills/paperclip/SKILL.md:643", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md#L626:full-reference", + "sourceAnchor": "skills/paperclip/SKILL.md#L643:full-reference", "heading": "Full Reference", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", @@ -668,207 +677,207 @@ "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:787", + "id": "skill:skills/paperclip/references/api-reference.md:807", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L787:option-a-one-call-create-with-workspace", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L807:option-a-one-call-create-with-workspace", "heading": "Option A: One-call create with workspace", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:806", + "id": "skill:skills/paperclip/references/api-reference.md:826", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L806:option-b-two-calls-project-first-then-workspace", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L826:option-b-two-calls-project-first-then-workspace", "heading": "Option B: Two calls (project first, then workspace)", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:835", + "id": "skill:skills/paperclip/references/api-reference.md:855", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L835:governance-and-approvals", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L855:governance-and-approvals", "heading": "Governance and Approvals", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, - { - "id": "skill:skills/paperclip/references/api-reference.md:839", - "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L839:requesting-a-hire-management-only", - "heading": "Requesting a hire (management only)", - "primaryDisposition": "optional_agent_tool", - "semanticOperation": "scoped_discovery", - "expectedMockState": "operation_result" - }, { "id": "skill:skills/paperclip/references/api-reference.md:859", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L859:ceo-strategy-approval", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L859:requesting-a-hire-management-only", + "heading": "Requesting a hire (management only)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:879", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L879:ceo-strategy-approval", "heading": "CEO strategy approval", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:868", + "id": "skill:skills/paperclip/references/api-reference.md:888", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L868:issue-thread-confirmations", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L888:issue-thread-confirmations", "heading": "Issue-thread confirmations", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:926", + "id": "skill:skills/paperclip/references/api-reference.md:946", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L926:checkbox-confirmations", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L946:checkbox-confirmations", "heading": "Checkbox confirmations", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1041", + "id": "skill:skills/paperclip/references/api-reference.md:1061", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1041:item-verdict-requests", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1061:item-verdict-requests", "heading": "Item verdict requests", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1151", + "id": "skill:skills/paperclip/references/api-reference.md:1171", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1151:checking-approval-status", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1171:checking-approval-status", "heading": "Checking approval status", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1157", + "id": "skill:skills/paperclip/references/api-reference.md:1177", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1157:approval-follow-up-requesting-agent", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1177:approval-follow-up-requesting-agent", "heading": "Approval follow-up (requesting agent)", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1175", + "id": "skill:skills/paperclip/references/api-reference.md:1195", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1175:issue-lifecycle", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1195:issue-lifecycle", "heading": "Issue Lifecycle", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1205", + "id": "skill:skills/paperclip/references/api-reference.md:1225", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1205:error-handling", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1225:error-handling", "heading": "Error Handling", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1219", + "id": "skill:skills/paperclip/references/api-reference.md:1239", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1219:full-api-reference", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1239:full-api-reference", "heading": "Full API Reference", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1221", + "id": "skill:skills/paperclip/references/api-reference.md:1241", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1221:agents", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1241:agents", "heading": "Agents", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1242", + "id": "skill:skills/paperclip/references/api-reference.md:1262", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1242:issues-tasks", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1262:issues-tasks", "heading": "Issues (Tasks)", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1282", + "id": "skill:skills/paperclip/references/api-reference.md:1302", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1282:companies-projects-goals", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1302:companies-projects-goals", "heading": "Companies, Projects, Goals", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1306", + "id": "skill:skills/paperclip/references/api-reference.md:1326", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1306:routines", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1326:routines", "heading": "Routines", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1322", + "id": "skill:skills/paperclip/references/api-reference.md:1342", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1322:approvals-costs-activity-dashboard", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1342:approvals-costs-activity-dashboard", "heading": "Approvals, Costs, Activity, Dashboard", "primaryDisposition": "control_plane_owned", "semanticOperation": "runtime_reconciliation", "expectedMockState": "runtime_decision_record" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1344", + "id": "skill:skills/paperclip/references/api-reference.md:1364", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1344:secrets", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1364:secrets", "heading": "Secrets", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1357", + "id": "skill:skills/paperclip/references/api-reference.md:1377", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1357:agent-secret-proposals", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1377:agent-secret-proposals", "heading": "Agent secret proposals", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1410", + "id": "skill:skills/paperclip/references/api-reference.md:1430", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1410:re-bind-an-existing-secret-under-a-new-path-no-secret-id", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1430:re-bind-an-existing-secret-under-a-new-path-no-secret-id", "heading": "Re-bind an existing secret under a new path (no secret ID)", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1457", + "id": "skill:skills/paperclip/references/api-reference.md:1477", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1457:agent-secret-access", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1477:agent-secret-access", "heading": "Agent secret access", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", "expectedMockState": "operation_result" }, { - "id": "skill:skills/paperclip/references/api-reference.md:1497", + "id": "skill:skills/paperclip/references/api-reference.md:1517", "kind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md#L1497:common-mistakes", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1517:common-mistakes", "heading": "Common Mistakes", "primaryDisposition": "optional_agent_tool", "semanticOperation": "scoped_discovery", diff --git a/packages/paperclip-runner/generated/capability/capability-contract.md b/packages/paperclip-runner/generated/capability/capability-contract.md index d5400fdfd0..f88c3625c7 100644 --- a/packages/paperclip-runner/generated/capability/capability-contract.md +++ b/packages/paperclip-runner/generated/capability/capability-contract.md @@ -2,9 +2,9 @@ Generated by `scripts/generate-capability-contract.mjs`; do not edit generated files. -- Skill/reference headings: 153 +- Skill/reference headings: 154 - Legacy MCP tools: 42 - Eval cases: 106 across 16 groups -- Deterministic content SHA-256: `6eefb67205ab07317bc8670ec7d39bce2f3aad38d8f376593ef0f61c18fb60c0` +- Deterministic content SHA-256: `cde4167fcd6990a386dfdfcd9e472005ff5b131a0056b3bd50363682c3ccc5b9` Every row has exactly one primary disposition, a source anchor, a semantic operation, and a mock-state expectation. diff --git a/packages/paperclip-runner/scripts/check-capability-inventory.test.mjs b/packages/paperclip-runner/scripts/check-capability-inventory.test.mjs index 89a35fee45..2b47541f0f 100644 --- a/packages/paperclip-runner/scripts/check-capability-inventory.test.mjs +++ b/packages/paperclip-runner/scripts/check-capability-inventory.test.mjs @@ -25,7 +25,7 @@ function row(id, group = "hb") { function validInventories() { const evaluations = Array.from({ length: 106 }, (_, index) => row(`eval-${index}`, capabilityGroups[index % capabilityGroups.length])); - const aliases = Array.from({ length: 41 }, (_, index) => ({ + const aliases = Array.from({ length: 42 }, (_, index) => ({ id: `mcp:tool-${index}`, name: `tool-${index}`, sourceAnchor: `source:${index + 1}`, @@ -42,7 +42,7 @@ function validInventories() { schemaVersion: 2, inventoryRole: "normative", generatedFrom: ["skills/paperclip/SKILL.md"], - rows: Array.from({ length: 152 }, (_, index) => row(`capability-${index}`)), + rows: Array.from({ length: 153 }, (_, index) => row(`capability-${index}`)), }, evaluations: { schemaVersion: 2, diff --git a/packages/paperclip-runner/scripts/lib/capability-inventory.mjs b/packages/paperclip-runner/scripts/lib/capability-inventory.mjs index a7bad6a246..2c7b60d17f 100644 --- a/packages/paperclip-runner/scripts/lib/capability-inventory.mjs +++ b/packages/paperclip-runner/scripts/lib/capability-inventory.mjs @@ -247,7 +247,7 @@ export async function buildMcpInventory(repoRoot) { export function validateInventories(inventories) { const errors = []; - const expectedCounts = { capabilities: 152, evaluations: 106, legacyMcpAliases: 42 }; + const expectedCounts = { capabilities: 153, evaluations: 106, legacyMcpAliases: 42 }; const normativeNames = ["capabilities", "evaluations"]; const normativeRows = new Map(); const globalNormativeIds = new Set(); diff --git a/packages/paperclip-runner/spec/capability/capabilities.yaml b/packages/paperclip-runner/spec/capability/capabilities.yaml index 71aa783178..947cc47bdd 100644 --- a/packages/paperclip-runner/spec/capability/capabilities.yaml +++ b/packages/paperclip-runner/spec/capability/capabilities.yaml @@ -59,11 +59,11 @@ ] }, { - "id": "skill:skills/paperclip/SKILL.md:the-heartbeat-procedure:30", + "id": "skill:skills/paperclip/SKILL.md:conversation-tasks:30", "sourceKind": "skill_heading", "sourceAnchor": "skills/paperclip/SKILL.md:30", - "title": "The Heartbeat Procedure", - "expectedSemantics": "Skill guidance headed “The Heartbeat Procedure”.", + "title": "Conversation tasks", + "expectedSemantics": "Skill guidance headed “Conversation tasks”.", "primaryDisposition": "optional_agent_tool", "requiredGrants": [], "assertionClasses": [ @@ -74,9 +74,24 @@ ] }, { - "id": "skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:101", + "id": "skill:skills/paperclip/SKILL.md:the-heartbeat-procedure:47", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:101", + "sourceAnchor": "skills/paperclip/SKILL.md:47", + "title": "The Heartbeat Procedure", + "expectedSemantics": "Skill guidance headed “The Heartbeat Procedure”.", + "primaryDisposition": "optional_agent_tool", + "requiredGrants": [], + "assertionClasses": [ + "control_plane_invariant" + ], + "evidenceIds": [ + "skill:skills/paperclip/SKILL.md:47" + ] + }, + { + "id": "skill:skills/paperclip/SKILL.md:generated-artifacts-and-work-products:118", + "sourceKind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md:118", "title": "Generated Artifacts and Work Products", "expectedSemantics": "Skill guidance headed “Generated Artifacts and Work Products”.", "primaryDisposition": "always_agent_tool", @@ -85,13 +100,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:101" + "skill:skills/paperclip/SKILL.md:118" ] }, { - "id": "skill:skills/paperclip/SKILL.md:status-quick-guide:148", + "id": "skill:skills/paperclip/SKILL.md:status-quick-guide:165", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:148", + "sourceAnchor": "skills/paperclip/SKILL.md:165", "title": "Status Quick Guide", "expectedSemantics": "Skill guidance headed “Status Quick Guide”.", "primaryDisposition": "control_plane_owned", @@ -100,13 +115,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:148" + "skill:skills/paperclip/SKILL.md:165" ] }, { - "id": "skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:158", + "id": "skill:skills/paperclip/SKILL.md:monitors-and-watchers-say-only-what-you-actually-scheduled:175", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:158", + "sourceAnchor": "skills/paperclip/SKILL.md:175", "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", @@ -115,13 +130,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:158" + "skill:skills/paperclip/SKILL.md:175" ] }, { - "id": "skill:skills/paperclip/SKILL.md:delegating-review-tasks:171", + "id": "skill:skills/paperclip/SKILL.md:delegating-review-tasks:188", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:171", + "sourceAnchor": "skills/paperclip/SKILL.md:188", "title": "Delegating review tasks", "expectedSemantics": "Skill guidance headed “Delegating review tasks”.", "primaryDisposition": "always_agent_tool", @@ -130,13 +145,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:171" + "skill:skills/paperclip/SKILL.md:188" ] }, { - "id": "skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:182", + "id": "skill:skills/paperclip/SKILL.md:managing-a-user-s-inbox:199", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:182", + "sourceAnchor": "skills/paperclip/SKILL.md:199", "title": "Managing A User's Inbox", "expectedSemantics": "Skill guidance headed “Managing A User's Inbox”.", "primaryDisposition": "control_plane_owned", @@ -145,13 +160,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:182" + "skill:skills/paperclip/SKILL.md:199" ] }, { - "id": "skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:190", + "id": "skill:skills/paperclip/SKILL.md:issue-dependencies-blockers:207", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:190", + "sourceAnchor": "skills/paperclip/SKILL.md:207", "title": "Issue Dependencies (Blockers)", "expectedSemantics": "Skill guidance headed “Issue Dependencies (Blockers)”.", "primaryDisposition": "control_plane_owned", @@ -160,13 +175,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:190" + "skill:skills/paperclip/SKILL.md:207" ] }, { - "id": "skill:skills/paperclip/SKILL.md:requesting-board-approval:215", + "id": "skill:skills/paperclip/SKILL.md:requesting-board-approval:232", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:215", + "sourceAnchor": "skills/paperclip/SKILL.md:232", "title": "Requesting Board Approval", "expectedSemantics": "Skill guidance headed “Requesting Board Approval”.", "primaryDisposition": "optional_agent_tool", @@ -175,13 +190,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:215" + "skill:skills/paperclip/SKILL.md:232" ] }, { - "id": "skill:skills/paperclip/SKILL.md:issue-thread-interactions:236", + "id": "skill:skills/paperclip/SKILL.md:issue-thread-interactions:253", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:236", + "sourceAnchor": "skills/paperclip/SKILL.md:253", "title": "Issue-Thread Interactions", "expectedSemantics": "Skill guidance headed “Issue-Thread Interactions”.", "primaryDisposition": "optional_agent_tool", @@ -190,13 +205,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:236" + "skill:skills/paperclip/SKILL.md:253" ] }, { - "id": "skill:skills/paperclip/SKILL.md:standalone-decisions:265", + "id": "skill:skills/paperclip/SKILL.md:standalone-decisions:282", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:265", + "sourceAnchor": "skills/paperclip/SKILL.md:282", "title": "Standalone Decisions", "expectedSemantics": "Skill guidance headed “Standalone Decisions”.", "primaryDisposition": "optional_agent_tool", @@ -205,13 +220,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:265" + "skill:skills/paperclip/SKILL.md:282" ] }, { - "id": "skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:369", + "id": "skill:skills/paperclip/SKILL.md:mcp-tool-approval-gates:386", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:369", + "sourceAnchor": "skills/paperclip/SKILL.md:386", "title": "MCP Tool Approval Gates", "expectedSemantics": "Skill guidance headed “MCP Tool Approval Gates”.", "primaryDisposition": "optional_agent_tool", @@ -220,13 +235,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:369" + "skill:skills/paperclip/SKILL.md:386" ] }, { - "id": "skill:skills/paperclip/SKILL.md:niche-workflow-pointers:411", + "id": "skill:skills/paperclip/SKILL.md:niche-workflow-pointers:428", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:411", + "sourceAnchor": "skills/paperclip/SKILL.md:428", "title": "Niche Workflow Pointers", "expectedSemantics": "Skill guidance headed “Niche Workflow Pointers”.", "primaryDisposition": "optional_agent_tool", @@ -235,13 +250,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:411" + "skill:skills/paperclip/SKILL.md:428" ] }, { - "id": "skill:skills/paperclip/SKILL.md:cases:421", + "id": "skill:skills/paperclip/SKILL.md:cases:438", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:421", + "sourceAnchor": "skills/paperclip/SKILL.md:438", "title": "Cases", "expectedSemantics": "Skill guidance headed “Cases”.", "primaryDisposition": "optional_agent_tool", @@ -250,13 +265,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:421" + "skill:skills/paperclip/SKILL.md:438" ] }, { - "id": "skill:skills/paperclip/SKILL.md:company-skills-workflow:426", + "id": "skill:skills/paperclip/SKILL.md:company-skills-workflow:443", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:426", + "sourceAnchor": "skills/paperclip/SKILL.md:443", "title": "Company Skills Workflow", "expectedSemantics": "Skill guidance headed “Company Skills Workflow”.", "primaryDisposition": "optional_agent_tool", @@ -265,13 +280,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:426" + "skill:skills/paperclip/SKILL.md:443" ] }, { - "id": "skill:skills/paperclip/SKILL.md:routines:437", + "id": "skill:skills/paperclip/SKILL.md:routines:454", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:437", + "sourceAnchor": "skills/paperclip/SKILL.md:454", "title": "Routines", "expectedSemantics": "Skill guidance headed “Routines”.", "primaryDisposition": "optional_agent_tool", @@ -280,13 +295,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:437" + "skill:skills/paperclip/SKILL.md:454" ] }, { - "id": "skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:448", + "id": "skill:skills/paperclip/SKILL.md:issue-workspace-runtime-controls:465", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:448", + "sourceAnchor": "skills/paperclip/SKILL.md:465", "title": "Issue Workspace Runtime Controls", "expectedSemantics": "Skill guidance headed “Issue Workspace Runtime Controls”.", "primaryDisposition": "optional_agent_tool", @@ -295,13 +310,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:448" + "skill:skills/paperclip/SKILL.md:465" ] }, { - "id": "skill:skills/paperclip/SKILL.md:proposing-credentials-safely:455", + "id": "skill:skills/paperclip/SKILL.md:proposing-credentials-safely:472", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:455", + "sourceAnchor": "skills/paperclip/SKILL.md:472", "title": "Proposing Credentials Safely", "expectedSemantics": "Skill guidance headed “Proposing Credentials Safely”.", "primaryDisposition": "optional_agent_tool", @@ -310,13 +325,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:455" + "skill:skills/paperclip/SKILL.md:472" ] }, { - "id": "skill:skills/paperclip/SKILL.md:reading-granted-secrets:462", + "id": "skill:skills/paperclip/SKILL.md:reading-granted-secrets:479", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:462", + "sourceAnchor": "skills/paperclip/SKILL.md:479", "title": "Reading Granted Secrets", "expectedSemantics": "Skill guidance headed “Reading Granted Secrets”.", "primaryDisposition": "optional_agent_tool", @@ -325,13 +340,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:462" + "skill:skills/paperclip/SKILL.md:479" ] }, { - "id": "skill:skills/paperclip/SKILL.md:critical-rules:488", + "id": "skill:skills/paperclip/SKILL.md:critical-rules:505", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:488", + "sourceAnchor": "skills/paperclip/SKILL.md:505", "title": "Critical Rules", "expectedSemantics": "Skill guidance headed “Critical Rules”.", "primaryDisposition": "optional_agent_tool", @@ -340,13 +355,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:488" + "skill:skills/paperclip/SKILL.md:505" ] }, { - "id": "skill:skills/paperclip/SKILL.md:comment-style-required:512", + "id": "skill:skills/paperclip/SKILL.md:comment-style-required:529", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:512", + "sourceAnchor": "skills/paperclip/SKILL.md:529", "title": "Comment Style (Required)", "expectedSemantics": "Skill guidance headed “Comment Style (Required)”.", "primaryDisposition": "always_agent_tool", @@ -355,13 +370,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:512" + "skill:skills/paperclip/SKILL.md:529" ] }, { - "id": "skill:skills/paperclip/SKILL.md:update:544", + "id": "skill:skills/paperclip/SKILL.md:update:561", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:544", + "sourceAnchor": "skills/paperclip/SKILL.md:561", "title": "Update", "expectedSemantics": "Skill guidance headed “Update”.", "primaryDisposition": "optional_agent_tool", @@ -370,13 +385,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:544" + "skill:skills/paperclip/SKILL.md:561" ] }, { - "id": "skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:554", + "id": "skill:skills/paperclip/SKILL.md:planning-required-when-planning-requested:571", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:554", + "sourceAnchor": "skills/paperclip/SKILL.md:571", "title": "Planning (Required when planning requested)", "expectedSemantics": "Skill guidance headed “Planning (Required when planning requested)”.", "primaryDisposition": "optional_agent_tool", @@ -385,13 +400,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:554" + "skill:skills/paperclip/SKILL.md:571" ] }, { - "id": "skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:587", + "id": "skill:skills/paperclip/SKILL.md:key-endpoints-hot-routes:604", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:587", + "sourceAnchor": "skills/paperclip/SKILL.md:604", "title": "Key Endpoints (Hot Routes)", "expectedSemantics": "Skill guidance headed “Key Endpoints (Hot Routes)”.", "primaryDisposition": "optional_agent_tool", @@ -400,13 +415,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:587" + "skill:skills/paperclip/SKILL.md:604" ] }, { - "id": "skill:skills/paperclip/SKILL.md:searching-issues:616", + "id": "skill:skills/paperclip/SKILL.md:searching-issues:633", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:616", + "sourceAnchor": "skills/paperclip/SKILL.md:633", "title": "Searching Issues", "expectedSemantics": "Skill guidance headed “Searching Issues”.", "primaryDisposition": "optional_agent_tool", @@ -415,13 +430,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:616" + "skill:skills/paperclip/SKILL.md:633" ] }, { - "id": "skill:skills/paperclip/SKILL.md:full-reference:626", + "id": "skill:skills/paperclip/SKILL.md:full-reference:643", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/SKILL.md:626", + "sourceAnchor": "skills/paperclip/SKILL.md:643", "title": "Full Reference", "expectedSemantics": "Skill guidance headed “Full Reference”.", "primaryDisposition": "optional_agent_tool", @@ -430,7 +445,7 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/SKILL.md:626" + "skill:skills/paperclip/SKILL.md:643" ] }, { @@ -1964,9 +1979,9 @@ ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:option-a-one-call-create-with-workspace:787", + "id": "skill:skills/paperclip/references/api-reference.md:option-a-one-call-create-with-workspace:807", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:787", + "sourceAnchor": "skills/paperclip/references/api-reference.md:807", "title": "Option A: One-call create with workspace", "expectedSemantics": "Skill guidance headed “Option A: One-call create with workspace”.", "primaryDisposition": "optional_agent_tool", @@ -1975,13 +1990,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:787" + "skill:skills/paperclip/references/api-reference.md:807" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:option-b-two-calls-project-first-then-workspace:806", + "id": "skill:skills/paperclip/references/api-reference.md:option-b-two-calls-project-first-then-workspace:826", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:806", + "sourceAnchor": "skills/paperclip/references/api-reference.md:826", "title": "Option B: Two calls (project first, then workspace)", "expectedSemantics": "Skill guidance headed “Option B: Two calls (project first, then workspace)”.", "primaryDisposition": "optional_agent_tool", @@ -1990,13 +2005,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:806" + "skill:skills/paperclip/references/api-reference.md:826" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:governance-and-approvals:835", + "id": "skill:skills/paperclip/references/api-reference.md:governance-and-approvals:855", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:835", + "sourceAnchor": "skills/paperclip/references/api-reference.md:855", "title": "Governance and Approvals", "expectedSemantics": "Skill guidance headed “Governance and Approvals”.", "primaryDisposition": "optional_agent_tool", @@ -2005,30 +2020,15 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:835" + "skill:skills/paperclip/references/api-reference.md:855" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:requesting-a-hire-management-only:839", - "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:839", - "title": "Requesting a hire (management only)", - "expectedSemantics": "Skill guidance headed “Requesting a hire (management only)”.", - "primaryDisposition": "optional_agent_tool", - "requiredGrants": [], - "assertionClasses": [ - "control_plane_invariant" - ], - "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:839" - ] - }, - { - "id": "skill:skills/paperclip/references/api-reference.md:ceo-strategy-approval:859", + "id": "skill:skills/paperclip/references/api-reference.md:requesting-a-hire-management-only:859", "sourceKind": "skill_heading", "sourceAnchor": "skills/paperclip/references/api-reference.md:859", - "title": "CEO strategy approval", - "expectedSemantics": "Skill guidance headed “CEO strategy approval”.", + "title": "Requesting a hire (management only)", + "expectedSemantics": "Skill guidance headed “Requesting a hire (management only)”.", "primaryDisposition": "optional_agent_tool", "requiredGrants": [], "assertionClasses": [ @@ -2039,9 +2039,24 @@ ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:issue-thread-confirmations:868", + "id": "skill:skills/paperclip/references/api-reference.md:ceo-strategy-approval:879", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:868", + "sourceAnchor": "skills/paperclip/references/api-reference.md:879", + "title": "CEO strategy approval", + "expectedSemantics": "Skill guidance headed “CEO strategy approval”.", + "primaryDisposition": "optional_agent_tool", + "requiredGrants": [], + "assertionClasses": [ + "control_plane_invariant" + ], + "evidenceIds": [ + "skill:skills/paperclip/references/api-reference.md:879" + ] + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:issue-thread-confirmations:888", + "sourceKind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md:888", "title": "Issue-thread confirmations", "expectedSemantics": "Skill guidance headed “Issue-thread confirmations”.", "primaryDisposition": "always_agent_tool", @@ -2050,13 +2065,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:868" + "skill:skills/paperclip/references/api-reference.md:888" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:checkbox-confirmations:926", + "id": "skill:skills/paperclip/references/api-reference.md:checkbox-confirmations:946", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:926", + "sourceAnchor": "skills/paperclip/references/api-reference.md:946", "title": "Checkbox confirmations", "expectedSemantics": "Skill guidance headed “Checkbox confirmations”.", "primaryDisposition": "always_agent_tool", @@ -2065,13 +2080,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:926" + "skill:skills/paperclip/references/api-reference.md:946" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:item-verdict-requests:1041", + "id": "skill:skills/paperclip/references/api-reference.md:item-verdict-requests:1061", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1041", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1061", "title": "Item verdict requests", "expectedSemantics": "Skill guidance headed “Item verdict requests”.", "primaryDisposition": "optional_agent_tool", @@ -2080,13 +2095,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1041" + "skill:skills/paperclip/references/api-reference.md:1061" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:checking-approval-status:1151", + "id": "skill:skills/paperclip/references/api-reference.md:checking-approval-status:1171", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1151", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1171", "title": "Checking approval status", "expectedSemantics": "Skill guidance headed “Checking approval status”.", "primaryDisposition": "optional_agent_tool", @@ -2095,13 +2110,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1151" + "skill:skills/paperclip/references/api-reference.md:1171" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:approval-follow-up-requesting-agent:1157", + "id": "skill:skills/paperclip/references/api-reference.md:approval-follow-up-requesting-agent:1177", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1157", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1177", "title": "Approval follow-up (requesting agent)", "expectedSemantics": "Skill guidance headed “Approval follow-up (requesting agent)”.", "primaryDisposition": "always_agent_tool", @@ -2110,13 +2125,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1157" + "skill:skills/paperclip/references/api-reference.md:1177" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:issue-lifecycle:1175", + "id": "skill:skills/paperclip/references/api-reference.md:issue-lifecycle:1195", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1175", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1195", "title": "Issue Lifecycle", "expectedSemantics": "Skill guidance headed “Issue Lifecycle”.", "primaryDisposition": "always_agent_tool", @@ -2125,13 +2140,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1175" + "skill:skills/paperclip/references/api-reference.md:1195" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:error-handling:1205", + "id": "skill:skills/paperclip/references/api-reference.md:error-handling:1225", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1205", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1225", "title": "Error Handling", "expectedSemantics": "Skill guidance headed “Error Handling”.", "primaryDisposition": "control_plane_owned", @@ -2140,13 +2155,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1205" + "skill:skills/paperclip/references/api-reference.md:1225" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:full-api-reference:1219", + "id": "skill:skills/paperclip/references/api-reference.md:full-api-reference:1239", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1219", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1239", "title": "Full API Reference", "expectedSemantics": "Skill guidance headed “Full API Reference”.", "primaryDisposition": "optional_agent_tool", @@ -2155,13 +2170,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1219" + "skill:skills/paperclip/references/api-reference.md:1239" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:agents:1221", + "id": "skill:skills/paperclip/references/api-reference.md:agents:1241", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1221", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1241", "title": "Agents", "expectedSemantics": "Skill guidance headed “Agents”.", "primaryDisposition": "optional_agent_tool", @@ -2170,13 +2185,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1221" + "skill:skills/paperclip/references/api-reference.md:1241" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:issues-tasks:1242", + "id": "skill:skills/paperclip/references/api-reference.md:issues-tasks:1262", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1242", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1262", "title": "Issues (Tasks)", "expectedSemantics": "Skill guidance headed “Issues (Tasks)”.", "primaryDisposition": "optional_agent_tool", @@ -2185,13 +2200,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1242" + "skill:skills/paperclip/references/api-reference.md:1262" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:companies-projects-goals:1282", + "id": "skill:skills/paperclip/references/api-reference.md:companies-projects-goals:1302", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1282", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1302", "title": "Companies, Projects, Goals", "expectedSemantics": "Skill guidance headed “Companies, Projects, Goals”.", "primaryDisposition": "optional_agent_tool", @@ -2200,13 +2215,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1282" + "skill:skills/paperclip/references/api-reference.md:1302" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:routines:1306", + "id": "skill:skills/paperclip/references/api-reference.md:routines:1326", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1306", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1326", "title": "Routines", "expectedSemantics": "Skill guidance headed “Routines”.", "primaryDisposition": "optional_agent_tool", @@ -2215,13 +2230,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1306" + "skill:skills/paperclip/references/api-reference.md:1326" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:approvals-costs-activity-dashboard:1322", + "id": "skill:skills/paperclip/references/api-reference.md:approvals-costs-activity-dashboard:1342", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1322", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1342", "title": "Approvals, Costs, Activity, Dashboard", "expectedSemantics": "Skill guidance headed “Approvals, Costs, Activity, Dashboard”.", "primaryDisposition": "optional_agent_tool", @@ -2230,13 +2245,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1322" + "skill:skills/paperclip/references/api-reference.md:1342" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:secrets:1344", + "id": "skill:skills/paperclip/references/api-reference.md:secrets:1364", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1344", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1364", "title": "Secrets", "expectedSemantics": "Skill guidance headed “Secrets”.", "primaryDisposition": "optional_agent_tool", @@ -2245,13 +2260,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1344" + "skill:skills/paperclip/references/api-reference.md:1364" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:agent-secret-proposals:1357", + "id": "skill:skills/paperclip/references/api-reference.md:agent-secret-proposals:1377", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1357", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1377", "title": "Agent secret proposals", "expectedSemantics": "Skill guidance headed “Agent secret proposals”.", "primaryDisposition": "optional_agent_tool", @@ -2260,13 +2275,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1357" + "skill:skills/paperclip/references/api-reference.md:1377" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:agent-secret-access:1457", + "id": "skill:skills/paperclip/references/api-reference.md:agent-secret-access:1477", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1457", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1477", "title": "Agent secret access", "expectedSemantics": "Skill guidance headed “Agent secret access”.", "primaryDisposition": "optional_agent_tool", @@ -2275,13 +2290,13 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1457" + "skill:skills/paperclip/references/api-reference.md:1477" ] }, { - "id": "skill:skills/paperclip/references/api-reference.md:common-mistakes:1497", + "id": "skill:skills/paperclip/references/api-reference.md:common-mistakes:1517", "sourceKind": "skill_heading", - "sourceAnchor": "skills/paperclip/references/api-reference.md:1497", + "sourceAnchor": "skills/paperclip/references/api-reference.md:1517", "title": "Common Mistakes", "expectedSemantics": "Skill guidance headed “Common Mistakes”.", "primaryDisposition": "optional_agent_tool", @@ -2290,7 +2305,7 @@ "control_plane_invariant" ], "evidenceIds": [ - "skill:skills/paperclip/references/api-reference.md:1497" + "skill:skills/paperclip/references/api-reference.md:1517" ] } ] diff --git a/packages/paperclip-runner/src/generated/capability-contract.ts b/packages/paperclip-runner/src/generated/capability-contract.ts index b750f66c7f..ba129500d2 100644 --- a/packages/paperclip-runner/src/generated/capability-contract.ts +++ b/packages/paperclip-runner/src/generated/capability-contract.ts @@ -6,9 +6,9 @@ export type CapabilityPrimaryDisposition = | "optional_agent_tool"; export const capabilityInventoryCounts = { - "skillReferenceCapabilities": 152, + "skillReferenceCapabilities": 153, "evalCases": 106, - "normativeRows": 258, + "normativeRows": 259, "legacyMcpAliases": 42 } as const; diff --git a/packages/paperclip-runner/test/capability-contract.test.mjs b/packages/paperclip-runner/test/capability-contract.test.mjs index b9cd7f3305..5fdf00cd43 100644 --- a/packages/paperclip-runner/test/capability-contract.test.mjs +++ b/packages/paperclip-runner/test/capability-contract.test.mjs @@ -17,7 +17,7 @@ test("generated Capability inventory has full source coverage", async () => { readRows("eval-traceability.yaml"), ]); - assert.equal(capabilities.length, 152); + assert.equal(capabilities.length, 154); assert.equal(tools.length, 42); assert.equal(evals.length, 106); assert.equal(new Set(evals.map((row) => row.group)).size, 16); diff --git a/server/src/__tests__/heartbeat-run-summary.test.ts b/server/src/__tests__/heartbeat-run-summary.test.ts index e1f970a091..7a08d022e6 100644 --- a/server/src/__tests__/heartbeat-run-summary.test.ts +++ b/server/src/__tests__/heartbeat-run-summary.test.ts @@ -447,6 +447,19 @@ describe("resolveHeartbeatRunResponse", () => { }); }); + it("persists a final assistant reply when the server completed a conversation turn", () => { + expect(resolveHeartbeatRunResponse({ + conversationTurnFinished: true, + resultJson: { nativeResult: { schema: "paperclip.run_result.v1", + reportedWorkDisposition: "yielded", summary: "Waiting for the next message." } }, + finalAgentMessage: { text: "Which project should own this?", + sourceEventId: "chat-final-1", channel: "final" }, + })).toMatchObject({ + text: "Which project should own this?", + decision: { commentAction: "create", sourceEventId: "chat-final-1" }, + }); + }); + it("does not render a serialized semantic result as the final prose", () => { expect( resolveHeartbeatRunResponse({ diff --git a/server/src/services/agent-conversations.ts b/server/src/services/agent-conversations.ts index a180864a16..d55555acd0 100644 --- a/server/src/services/agent-conversations.ts +++ b/server/src/services/agent-conversations.ts @@ -61,7 +61,7 @@ export const AGENT_CHAT_DIRECTIVE = `You are in an ongoing conversation with the Research, clarify, and develop full plans here using the conversation's plan document. Revise the draft as the discussion develops. Planning alone does not create execution tasks. Put implementation and substantial execution into separate tasks. -Before handing off work, inspect available projects and repositories. Every task you create from this chat must belong to a suitable project. Reuse an appropriate existing project; otherwise use create_project. Consider all relevant available repositories and pass repositoryIds for one or multiple repositories when the work spans them. For existing GitHub repositories you can access that are absent from the catalog, pass their HTTPS repositoryUrls; this registers them with the project without creating remote GitHub repositories. You may combine known IDs and URLs and attach multiple repositories. Never invent repository IDs or substitute inaccessible repositories. Ask when the choice is materially ambiguous or required access is missing. Non-code projects may need no repository. +Before handing off work, inspect available projects and repositories. Every task you create from this chat must belong to a suitable project. Reuse an appropriate existing project; otherwise use create_project. Consider all relevant available repositories and pass repositoryIds for one or multiple repositories when the work spans them. For existing GitHub repositories you can access that are absent from the catalog, pass their HTTPS repositoryUrls; this registers them with the project without creating remote GitHub repositories. You may combine known IDs and URLs and attach multiple repositories. The direct HTTP equivalent is POST /api/companies/{companyId}/projects with name, repositoryIds and/or repositoryUrls arrays, and an idempotencyKey. Include all selected repositories in that creation; do not combine these arrays with workspace. Never invent repository IDs or substitute inaccessible repositories. Ask when the choice is materially ambiguous or required access is missing. Non-code projects may need no repository. Create ordinary assigned tasks, never subtasks of this conversation. Give each task a clear outcome, context, acceptance criteria, project, and appropriate assignee. Use create_task with initialPlan to copy the relevant plan into the new task before execution starts. If using the HTTP API directly, POST /api/companies/{companyId}/issues with projectId, assigneeAgentId, status: "todo", initialPlan containing the relevant plan Markdown, and an idempotencyKey; omit parentId. Putting a plan in description does not create the task's plan document. Verify the new task's plan document before claiming the handoff is complete. Preserve the original plan here. When splitting work, include the relevant part of the plan in each task. Create and link each task before claiming it exists. diff --git a/server/src/services/heartbeat-run-summary.ts b/server/src/services/heartbeat-run-summary.ts index 69f21f22cc..26c82c5977 100644 --- a/server/src/services/heartbeat-run-summary.ts +++ b/server/src/services/heartbeat-run-summary.ts @@ -315,6 +315,8 @@ function decision( */ export function resolveHeartbeatRunResponse(input: { resultJson: Record | null | undefined; + /** Server-owned conversation finalization, after normal governance checks. */ + conversationTurnFinished?: boolean; existingComment?: { id: string; body?: string | null } | null; finalAgentMessage?: { text: string; @@ -340,7 +342,7 @@ export function resolveHeartbeatRunResponse(input: { // still emit terminal-looking prose while the control plane is yielding for // an interaction; keep that prose in activity and let the durable // interaction own the visible waiting state. - if (hasYieldedSemanticResult(resultJson)) { + if (hasYieldedSemanticResult(resultJson) && !input.conversationTurnFinished) { return { text: null, decision: decision("none", { diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 66f648f2bd..2c593a77a1 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -21773,6 +21773,8 @@ export function heartbeatService( ); const resolved = resolveHeartbeatRunResponse({ resultJson: persistedResultJson, + conversationTurnFinished: isConversation(issueContext) && + persistedResultJson?.finalizationReasonCode === "conversation_turn_finished", existingComment: existingRunComment, finalAgentMessage, }); diff --git a/server/src/services/native-runtime/native-session-executor.test.ts b/server/src/services/native-runtime/native-session-executor.test.ts index 046fce2c86..9af86a844d 100644 --- a/server/src/services/native-runtime/native-session-executor.test.ts +++ b/server/src/services/native-runtime/native-session-executor.test.ts @@ -192,6 +192,7 @@ import { nativeSessionFailureSourceCode, nativeSessionRecoveryProjection, nativeGovernedWaitResult, + nativeConversationReplyResult, nativeToolsRefreshWaitResult, parseRemoteExecutableCandidate, buildRemoteCodexLauncherCommand, @@ -2188,6 +2189,55 @@ describe("provider plan synchronization", () => { }); }); +describe("native conversation replies", () => { + const reply: PrpEvent = { + schema: "paperclip.prp.event.v1", sourceInstanceId: "runner-1", + sourceEventId: "runner-1:run-1:8", sourceSeq: 8, sourceKind: "runner", + runId: "run-1", normalizedSessionId: "session-1", turnId: "turn-1", + eventType: "item.completed", schemaVersion: 1, priority: 1, + emittedAt: "2026-09-11T18:00:00.000Z", + payload: { kind: "agentMessage", channel: "final", text: "Which project should own this?" }, + }; + const terminal = { ...reply, sourceEventId: "runner-1:run-1:9", sourceSeq: 9, + eventType: "turn.completed", payload: { status: "completed" } } as PrpEvent; + const input = { conversation: true, replyEvent: reply, terminalEvent: terminal, + completionContract: { revision: "1", objective: "Ongoing conversation", + criteria: [{ id: "objective", requirement: "Help the user" }] } }; + + it("yields an evidenced completed reply without claiming execution completion", () => { + expect(nativeConversationReplyResult(input)).toMatchObject({ + reportedWorkDisposition: "yielded", summary: "Which project should own this?", + completionClaim: { objectiveSatisfied: false, criteria: [{ status: "unknown" }] }, + evidence: [{ ref: "run-event:runner-1:run-1:8" }], + continuation: { kind: "response_wake" }, attentionRequests: [], + }); + }); + + it.each(["turn.failed", "turn.cancelled", "turn.interrupted"])( + "does not reinterpret a %s provider turn as a chat reply", (eventType) => { + expect(nativeConversationReplyResult({ ...input, + terminalEvent: { ...terminal, eventType } as PrpEvent })).toBeNull(); + }, + ); + + it("keeps ordinary execution tasks and absent or unfinished replies fail-closed", () => { + expect(nativeConversationReplyResult({ ...input, conversation: false })).toBeNull(); + expect(nativeConversationReplyResult({ ...input, replyEvent: null })).toBeNull(); + for (const payload of [ + { kind: "agentMessage", channel: "progress", text: "Still working" }, + { kind: "agentMessage", channel: "final", text: " " }, + { kind: "toolCall", channel: "final", text: "Tool result" }, + ]) expect(nativeConversationReplyResult({ ...input, replyEvent: { ...reply, payload } })).toBeNull(); + }); + + it.each(["runId", "turnId", "normalizedSessionId"] as const)( + "rejects a final message from another %s", (key) => { + expect(nativeConversationReplyResult({ ...input, + replyEvent: { ...reply, [key]: "old-authority" } })).toBeNull(); + }, + ); +}); + describe("native governed waits", () => { it("yields to an existing tools-refresh wake without claiming completion or a human interaction", () => { const result = nativeToolsRefreshWaitResult({ wakeId: "wake-1", key: "connection-intent:tools:run-1:digest", diff --git a/server/src/services/native-runtime/native-session-executor.ts b/server/src/services/native-runtime/native-session-executor.ts index bbd921104e..533e751271 100644 --- a/server/src/services/native-runtime/native-session-executor.ts +++ b/server/src/services/native-runtime/native-session-executor.ts @@ -845,6 +845,46 @@ export function nativeGovernedWaitResult(input: { }; } +/** A completed chat reply yields to the next message without claiming task completion. */ +export function nativeConversationReplyResult(input: { + conversation: boolean; + terminalEvent: PrpEvent; + replyEvent: PrpEvent | null; + completionContract: NativeExecutionInput["completionContract"]["contract"]; +}): PrpStructuredRunResult | null { + const reply = input.replyEvent; + const payload = record(reply?.payload); + const text = typeof payload.text === "string" ? payload.text.trim() : ""; + if (!input.conversation || input.terminalEvent.eventType !== "turn.completed" || + !reply || reply.eventType !== "item.completed" || payload.kind !== "agentMessage" || + payload.channel !== "final" || !text || reply.runId !== input.terminalEvent.runId || + reply.turnId !== input.terminalEvent.turnId || + reply.normalizedSessionId !== input.terminalEvent.normalizedSessionId) return null; + const ref = `run-event:${reply.sourceEventId}`; + return { + schema: "paperclip.run_result.v1", + reportedWorkDisposition: "yielded", + summary: text.slice(0, 12_000), + completionClaim: { + contractRevision: input.completionContract.revision, + objectiveSatisfied: false, + criteria: input.completionContract.criteria.map((criterion) => ({ + criterionId: criterion.id, status: "unknown", evidenceRefs: [ref], + })), + remainingWork: [], + }, + evidence: [{ ref }], + verification: [], + attentionRequests: [], + artifacts: [], + continuation: { + kind: "response_wake", + summary: "Wait for the next user message in this conversation.", + idempotencyKey: `conversation-reply:${reply.sourceEventId}`, + }, + }; +} + /** * Bridge an asynchronous durable-interaction lookup to the runner package's * synchronous governed-wait boundary. Observations are single-use and bound @@ -4439,6 +4479,7 @@ async function executePaperclipNativeSessionWithinScope( payload: event.payload, }, ); + let completedConversationReply: PrpEvent | null = null; const controlPlane = new PaperclipControlPlanePort( input.db, { @@ -4454,6 +4495,11 @@ async function executePaperclipNativeSessionWithinScope( }, { onCommittedEvent: async (event) => { + if (event.eventType === "item.completed" && + record(event.payload).kind === "agentMessage" && + record(event.payload).channel === "final") { + completedConversationReply = event; + } await projectSessionGoalEvent(event); const eventAtMs = Date.parse(event.emittedAt); const milestoneAtMs = Number.isFinite(eventAtMs) @@ -4886,12 +4932,25 @@ async function executePaperclipNativeSessionWithinScope( resolveGovernedWait: ({ event }) => governedWaitObservation.consume(event), resolveMissingResult: async ({ terminalEvent }) => { - // A model may correctly create a durable question/confirmation and - // then end its provider turn without also invoking paperclip_finish. - // Recover only completed turns with a pending interaction created by - // this exact run; unrelated or failed turns still fail closed. + // Governed waits take precedence over an ordinary chat reply. + // Execution tasks still require their normal semantic finish. if (terminalEvent.eventType !== "turn.completed") return null; - return resolvePendingGovernedWait(); + const governedWait = await resolvePendingGovernedWait(); + if (governedWait) return governedWait; + const [conversation] = await input.db + .select({ agentId: issues.conversationAgentId }) + .from(issues) + .where(and( + eq(issues.id, input.execution.binding.issueId), + eq(issues.companyId, input.execution.binding.companyId), + )) + .limit(1); + return nativeConversationReplyResult({ + conversation: conversation?.agentId === input.execution.binding.agentId, + terminalEvent, + replyEvent: completedConversationReply, + completionContract: input.execution.completionContract.contract, + }); }, existingSession: existingWarmSession, persistedSession: persistedWarmSession, diff --git a/skills/paperclip/references/api-reference.md b/skills/paperclip/references/api-reference.md index 7c618cc2c1..e7a9fc5371 100644 --- a/skills/paperclip/references/api-reference.md +++ b/skills/paperclip/references/api-reference.md @@ -784,6 +784,26 @@ PATCH /api/agents/{agentId}/instructions-path When a CEO/manager task asks you to "set up a new project" and wire local + GitHub context, use this sequence. +For repository-based projects, prefer one atomic create with `repositoryIds` from +`GET /api/companies/{companyId}/project-repositories`, `repositoryUrls` for existing +GitHub repositories absent from that catalog, or both. These arrays support +multiple repositories. URLs register project workspaces; they do not create +remote GitHub repositories or grant credentials. Use HTTPS URLs without credentials. +Do not combine either array with an explicit `workspace`. Reuse the same +`idempotencyKey` and body when retrying a creation. + +``` +POST /api/companies/{companyId}/projects +{ + "name": "Web and API", + "repositoryUrls": ["https://github.com/acme/web", "https://github.com/acme/api"], + "idempotencyKey": "web-api-project" +} +``` + +Omit repository inputs for non-code work. The explicit workspace alternatives +below remain available when local workspace configuration is needed. + ### Option A: One-call create with workspace ``` @@ -1291,7 +1311,7 @@ Terminal states: `done`, `cancelled` | POST | `/api/companies/:companyId/archive` | Archive company | | GET | `/api/companies/:companyId/projects` | List projects | | GET | `/api/projects/:projectId` | Project details | -| POST | `/api/companies/:companyId/projects` | Create project (optional inline `workspace`) | +| POST | `/api/companies/:companyId/projects` | Create project (`repositoryIds`/`repositoryUrls` arrays or inline `workspace`; optional `idempotencyKey`) | | PATCH | `/api/projects/:projectId` | Update project | | GET | `/api/projects/:projectId/workspaces` | List project workspaces | | POST | `/api/projects/:projectId/workspaces` | Create project workspace | diff --git a/ui/src/components/TaskChatThread.tsx b/ui/src/components/TaskChatThread.tsx index 35aa227b83..d16c77026f 100644 --- a/ui/src/components/TaskChatThread.tsx +++ b/ui/src/components/TaskChatThread.tsx @@ -1,5 +1,5 @@ import type { ActivityEvent } from "@paperclipai/shared"; -import { projectCreatedItems } from "./task-chat/project-created-items"; +import { useProjectCreatedItems } from "@/hooks/useProjectCreatedItems"; import { requiresExecutionReconciliation } from "@paperclipai/shared"; import { TaskChatExpansionState } from "@/components/task-chat/expansion-state"; import { TaskChatScrollReady } from "@/components/task-chat/scroll-navigation"; @@ -538,6 +538,7 @@ export function TaskChatThread(props: TaskChatThreadProps) { resumeAssigneePending = false, } = props; const queryClient = useQueryClient(); + const createdProjectItems = useProjectCreatedItems(props.creationActivity ?? [], companyId); const [pendingComposerAssignee, setPendingComposerAssignee] = useState< string | null >(null); @@ -1253,14 +1254,14 @@ export function TaskChatThread(props: TaskChatThreadProps) { }, }); } - for (const item of projectCreatedItems(props.creationActivity ?? [])) { + for (const item of createdProjectItems) { entries.push({ id: item.id, item, ms: toMs(item.timestamp), order: 2 }); } return entries.sort( (a, b) => a.ms - b.ms || a.order - b.order || a.id.localeCompare(b.id), ); }, [ - props.creationActivity, + createdProjectItems, comments, projectedComments, commentItems, diff --git a/ui/src/components/task-chat/project-created-items.test.ts b/ui/src/components/task-chat/project-created-items.test.ts index b920149505..29ce4de201 100644 --- a/ui/src/components/task-chat/project-created-items.test.ts +++ b/ui/src/components/task-chat/project-created-items.test.ts @@ -1,24 +1,125 @@ import { describe, expect, it } from "vitest"; import type { ActivityEvent } from "@paperclipai/shared"; import { projectCreatedItems } from "./project-created-items"; -const event = (overrides: Partial = {}): ActivityEvent => ({ - id: "activity", companyId: "company", actorType: "agent", actorId: "agent", agentId: "agent", runId: "run", - action: "project.created", entityType: "project", entityId: "project", createdAt: new Date("2026-09-11T12:00:00Z"), - details: { name: "Launch", repositories: [{ id: "1", name: "org/app", url: "https://github.com/org/app" }, { id: "2", name: "org/docs", url: "https://github.com/org/docs" }] }, ...overrides, -} as ActivityEvent); +const event = (overrides: Partial = {}): ActivityEvent => + ({ + id: "activity", + companyId: "company", + actorType: "agent", + actorId: "agent", + agentId: "agent", + runId: "run", + action: "project.created", + entityType: "project", + entityId: "project", + createdAt: new Date("2026-09-11T12:00:00Z"), + details: { + name: "Launch", + repositories: [ + { id: "1", name: "org/app", url: "https://github.com/org/app" }, + { id: "2", name: "org/docs", url: "https://github.com/org/docs" }, + ], + }, + ...overrides, + }) as ActivityEvent; describe("durable project creation feed", () => { it("deduplicates repeated receipts while retaining all repositories", () => { const items = projectCreatedItems([event(), event({ id: "replay" })]); expect(items).toHaveLength(1); - expect(items[0]).toMatchObject({ kind: "project_created", projectId: "project", name: "Launch" }); + expect(items[0]).toMatchObject({ + kind: "project_created", + projectId: "project", + name: "Launch", + }); expect(items[0].repositories).toHaveLength(2); - expect(projectCreatedItems(JSON.parse(JSON.stringify([event()])))).toEqual(items); + expect(projectCreatedItems(JSON.parse(JSON.stringify([event()])))).toEqual( + items, + ); }); it("does not turn a failed tool call or agent claim into a success card", () => { - expect(projectCreatedItems([event({ action: "runner.api_called" }), event({ action: "issue.comment_added" })])).toEqual([]); + expect( + projectCreatedItems([ + event({ action: "runner.api_called" }), + event({ action: "issue.comment_added" }), + ]), + ).toEqual([]); }); it("omits unsafe repository links and accepts projects without repositories", () => { - expect(projectCreatedItems([event({ details: { name: "Research", repositories: [{ id: "unsafe", name: "unsafe", url: "javascript:alert(1)" }] } })])[0].repositories).toEqual([]); - expect(projectCreatedItems([event({ details: { name: "Research" } })])[0].name).toBe("Research"); + expect( + projectCreatedItems([ + event({ + details: { + name: "Research", + repositories: [ + { id: "unsafe", name: "unsafe", url: "javascript:alert(1)" }, + ], + }, + }), + ])[0].repositories, + ).toEqual([]); + expect( + projectCreatedItems([event({ details: { name: "Research" } })])[0].name, + ).toBe("Research"); + }); +}); + +describe("project creation repository hydration", () => { + const project = { + id: "project", + companyId: "company", + workspaces: [ + { id: "workspace-1", name: "App", repoUrl: "https://github.com/org/app" }, + { + id: "workspace-2", + name: "Docs", + repoUrl: "https://github.com/org/docs", + }, + ], + } as import("@paperclipai/shared").Project; + + it("includes repositories added after creation without adding another card", () => { + const receipt = event({ + details: { + name: "Launch", + repositories: [ + { id: "1", name: "App", url: "https://github.com/org/app" }, + ], + }, + }); + const items = projectCreatedItems([receipt, receipt], [project]); + expect(items).toHaveLength(1); + expect(items[0].repositories.map((repo) => repo.url)).toEqual([ + "https://github.com/org/app", + "https://github.com/org/docs", + ]); + expect(projectCreatedItems([], [project])).toEqual([]); + expect(items[0].timestamp).toBe("2026-09-11T12:00:00.000Z"); + }); + + it("reflects repository removal but falls back when current project data is unavailable", () => { + expect( + projectCreatedItems([event()], [{ ...project, workspaces: [] }])[0] + .repositories, + ).toEqual([]); + expect( + projectCreatedItems( + [event()], + [{ ...project, companyId: "other-company" }], + ), + ).toEqual(projectCreatedItems([event()])); + }); + + it("deduplicates workspaces and rejects unsafe current repository links", () => { + const workspaces = [ + project.workspaces[0], + project.workspaces[0], + { ...project.workspaces[1], repoUrl: "javascript:alert(1)" }, + ]; + expect( + projectCreatedItems([event()], [{ ...project, workspaces }])[0] + .repositories, + ).toEqual([ + { id: "workspace-1", name: "App", url: "https://github.com/org/app" }, + ]); }); }); diff --git a/ui/src/components/task-chat/project-created-items.ts b/ui/src/components/task-chat/project-created-items.ts index a82eff78f9..d86f79d815 100644 --- a/ui/src/components/task-chat/project-created-items.ts +++ b/ui/src/components/task-chat/project-created-items.ts @@ -1,21 +1,61 @@ -import type { ActivityEvent } from "@paperclipai/shared"; +import type { ActivityEvent, Project } from "@paperclipai/shared"; import type { TaskChatProjectCreatedItem } from "./task-chat-model"; -export function projectCreatedItems(events: readonly ActivityEvent[]): TaskChatProjectCreatedItem[] { +export function projectCreatedItems( + events: readonly ActivityEvent[], + projects: readonly Pick[] = [], +): TaskChatProjectCreatedItem[] { const seen = new Set(); - return events.flatMap(event => { - if (event.action !== "project.created" || event.entityType !== "project" || seen.has(event.entityId)) return []; + return events.flatMap((event) => { + if ( + event.action !== "project.created" || + event.entityType !== "project" || + seen.has(event.entityId) + ) + return []; const details = event.details ?? {}; if (typeof details.name !== "string") return []; seen.add(event.entityId); - const repositories = Array.isArray(details.repositories) ? details.repositories.flatMap(value => { - if (!value || typeof value !== "object") return []; - const repo = value as Record; - if (typeof repo.id !== "string" || typeof repo.name !== "string" || typeof repo.url !== "string" || !/^https:\/\//.test(repo.url)) return []; - return [{ id: repo.id, name: repo.name, url: repo.url }]; - }) : []; - return [{ id: `project-created:${event.entityId}`, kind: "project_created" as const, projectId: event.entityId, - name: details.name, description: typeof details.description === "string" ? details.description : null, - repositories, timestamp: new Date(event.createdAt).toISOString() }]; + const project = projects.find( + (candidate) => + candidate.id === event.entityId && + candidate.companyId === event.companyId, + ); + const repositoryDetails = project + ? project.workspaces.map((workspace) => ({ + id: workspace.id, + name: workspace.name, + url: workspace.repoUrl, + })) + : details.repositories; + const seenUrls = new Set(); + const repositories = Array.isArray(repositoryDetails) + ? repositoryDetails.flatMap((value) => { + if (!value || typeof value !== "object") return []; + const repo = value as Record; + if ( + typeof repo.id !== "string" || + typeof repo.name !== "string" || + typeof repo.url !== "string" || + !/^https:\/\//.test(repo.url) + ) + return []; + if (seenUrls.has(repo.url)) return []; + seenUrls.add(repo.url); + return [{ id: repo.id, name: repo.name, url: repo.url }]; + }) + : []; + return [ + { + id: `project-created:${event.entityId}`, + kind: "project_created" as const, + projectId: event.entityId, + name: details.name, + description: + typeof details.description === "string" ? details.description : null, + repositories, + timestamp: new Date(event.createdAt).toISOString(), + }, + ]; }); } diff --git a/ui/src/hooks/useProjectCreatedItems.test.tsx b/ui/src/hooks/useProjectCreatedItems.test.tsx new file mode 100644 index 0000000000..095405b10a --- /dev/null +++ b/ui/src/hooks/useProjectCreatedItems.test.tsx @@ -0,0 +1,109 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { ActivityEvent, Project } from "@paperclipai/shared"; +import { projectsApi } from "@/api/projects"; +import { queryKeys } from "@/lib/queryKeys"; +import { useProjectCreatedItems } from "./useProjectCreatedItems"; + +vi.mock("@/api/projects", () => ({ projectsApi: { get: vi.fn() } })); +const receipt = { + id: "receipt", + companyId: "company", + action: "project.created", + entityType: "project", + entityId: "project", + createdAt: "2026-09-11T12:00:00Z", + details: { + name: "Launch", + repositories: [ + { id: "repo", name: "Original", url: "https://github.com/org/app" }, + ], + }, +} as unknown as ActivityEvent; +const events = [receipt]; +let root: Root; +let container: HTMLDivElement; +let client: QueryClient; +function Fixture({ activity = events }: { activity?: ActivityEvent[] }) { + const items = useProjectCreatedItems(activity, "company"); + return
{JSON.stringify(items)}
; +} +function render(activity = events) { + act(() => + root.render( + + + , + ), + ); +} +beforeEach(() => { + vi.clearAllMocks(); + container = document.createElement("div"); + document.body.append(container); + root = createRoot(container); + client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); +}); +afterEach(() => { + act(() => root.unmount()); + client.clear(); + container.remove(); +}); + +describe("shared project creation card queries", () => { + it("refreshes repositories through existing project invalidation", async () => { + const project = { + id: "project", + companyId: "company", + workspaces: [ + { id: "first", name: "App", repoUrl: "https://github.com/org/app" }, + ], + } as Project; + vi.mocked(projectsApi.get).mockResolvedValue(project); + render(); + await vi.waitFor(() => + expect(container.textContent).toContain('"name":"App"'), + ); + expect(projectsApi.get).toHaveBeenCalledWith("project", "company"); + vi.mocked(projectsApi.get).mockResolvedValue({ + ...project, + workspaces: [ + ...project.workspaces, + { + ...project.workspaces[0], + id: "second", + name: "Docs", + repoUrl: "https://github.com/org/docs", + }, + ], + }); + await act(async () => { + await client.invalidateQueries({ + queryKey: queryKeys.projects.detail("project"), + }); + }); + await vi.waitFor(() => + expect(container.textContent).toContain("https://github.com/org/docs"), + ); + expect(JSON.parse(container.textContent!)).toHaveLength(1); + }); + it("retains the creation receipt if the project is inaccessible", async () => { + vi.mocked(projectsApi.get).mockRejectedValue(new Error("Not found")); + render(); + await vi.waitFor(() => + expect( + client.getQueryState(queryKeys.projects.detail("project"))?.status, + ).toBe("error"), + ); + expect(container.textContent).toContain('"name":"Original"'); + expect(projectsApi.get).toHaveBeenCalledTimes(1); + }); + it("does not query projects or invent creation cards without receipts", () => { + render([]); + expect(projectsApi.get).not.toHaveBeenCalled(); + expect(container.textContent).toBe("[]"); + }); +}); diff --git a/ui/src/hooks/useProjectCreatedItems.ts b/ui/src/hooks/useProjectCreatedItems.ts new file mode 100644 index 0000000000..880cbae9c1 --- /dev/null +++ b/ui/src/hooks/useProjectCreatedItems.ts @@ -0,0 +1,33 @@ +import { useMemo } from "react"; +import { useQueries, type UseQueryResult } from "@tanstack/react-query"; +import type { ActivityEvent, Project } from "@paperclipai/shared"; +import { projectsApi } from "@/api/projects"; +import { queryKeys } from "@/lib/queryKeys"; +import { projectCreatedItems } from "@/components/task-chat/project-created-items"; + +function availableProjects(results: UseQueryResult[]) { + return results.flatMap((result) => (result.isSuccess ? [result.data] : [])); +} + +/** Creation receipts establish the cards; authorized project reads keep their + * repository lists current as the same run adds or edits workspaces. + */ +export function useProjectCreatedItems( + events: readonly ActivityEvent[], + companyId?: string | null, +) { + const receipts = useMemo(() => projectCreatedItems(events), [events]); + const projects = useQueries({ + queries: receipts.map((receipt) => ({ + queryKey: queryKeys.projects.detail(receipt.projectId), + queryFn: () => projectsApi.get(receipt.projectId, companyId!), + enabled: Boolean(companyId), + retry: false, + })), + combine: availableProjects, + }); + return useMemo( + () => projectCreatedItems(events, projects), + [events, projects], + ); +}