diff --git a/packages/paperclip-runner/docs/phase-07-semantic-catalog.md b/packages/paperclip-runner/docs/phase-07-semantic-catalog.md new file mode 100644 index 00000000000..a91ccb5cde6 --- /dev/null +++ b/packages/paperclip-runner/docs/phase-07-semantic-catalog.md @@ -0,0 +1,79 @@ +# Phase 7 semantic catalog and authorization + +Phase 7D adds a transport-neutral tool boundary over the deterministic mock +`ControlPlanePort`. It does not contain Paperclip REST routes, authentication +headers, credentials, ACPX code, or a real control-plane binding. + +## Public boundary + +Import the catalog, policy, dispatcher, and binding helpers from the package +root: + +```ts +import { + Phase7SemanticDispatcher, + createPhase7ProviderNeutralBinding, +} from "@paperclipai/paperclip-runner"; + +const dispatcher = new Phase7SemanticDispatcher(mockPort, { + scenario: { + id: "dependency-manager", + claims: ["dependencies:write"], + }, + explicitClaims: ["dependencies:write"], +}); + +const exposed = dispatcher.listTools(runId); +const result = await dispatcher.dispatch({ + runId, + callId: "tool-call-1", + operationId: "set_dependencies", + input: { + idempotencyKey: "dependency-change-1", + blockedByTaskIds: ["task-2"], + }, +}); +``` + +`createPhase7ProviderNeutralBinding("fake")` and +`createPhase7ProviderNeutralBinding("live_codex")` return identical contract +arrays. Only the binding label differs. Track 7E can translate those definitions +to Codex tool definitions and pass calls to the dispatcher without changing +operation IDs, schemas, claims, or result shapes. + +## Authorization order + +Exposure and invocation both evaluate the actor, active-task ownership and +mode, scenario allow/deny rules, role restrictions, run claims, and explicit +claims. Optional operations require their descriptor claim to be present in the +intersection authorized for the run. The dispatcher repeats the evaluation +immediately before every mock read or command; a tool that was exposed earlier +can still be denied after a claim or ownership change. + +Unauthorized optional tools are absent from `listTools`. Direct calls receive a +`paperclip.semantic-denial.v1` result. The mock command boundary independently +checks command claims and task ownership, so bypassing catalog exposure does not +bypass authorization. + +The generic API escape hatch is an optional `skill_test` descriptor and is +disabled by default. When a scenario and explicit claim enable it, only two +read-only package-local paths are accepted: `/mock/state/revision` and +`/mock/task`. + +## Protected data + +Tool schemas contain no credential fields. Inputs containing protected keys or +credential-shaped values are rejected before mutation. Read results, denials, +and immutable semantic authorization records pass through the same recursive +redactor. Mock actor discovery also omits capability and budget internals. + +Generate and verify the checked-in contracts with: + +```sh +pnpm --dir packages/paperclip-runner generate:semantic-contracts +pnpm --dir packages/paperclip-runner check:semantic-contracts +``` + +The generated contract is +`generated/phase-07/semantic-tool-contracts.json`. Phase 7A traceability remains +under `generated/phase-07/` and is checked with `check:phase-07-contract`. diff --git a/packages/paperclip-runner/generated/phase-07/capabilities.yaml b/packages/paperclip-runner/generated/phase-07/capabilities.yaml new file mode 100644 index 00000000000..d1d5c2a0d50 --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/capabilities.yaml @@ -0,0 +1,1373 @@ +{ + "schemaVersion": 1, + "rows": [ + { + "id": "skill:skills/paperclip/SKILL.md:10", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L10:paperclip-skill", + "heading": "Paperclip Skill", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:14", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L14:terminology", + "heading": "Terminology", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:18", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L18:authentication", + "heading": "Authentication", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:28", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L28:the-heartbeat-procedure", + "heading": "The Heartbeat Procedure", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:99", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L99: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:144", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L144:status-quick-guide", + "heading": "Status Quick Guide", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:154", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L154: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:167", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L167:delegating-review-tasks", + "heading": "Delegating review tasks", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:178", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L178: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:186", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L186:issue-dependencies-blockers", + "heading": "Issue Dependencies (Blockers)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:211", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L211:requesting-board-approval", + "heading": "Requesting Board Approval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:232", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L232:issue-thread-interactions", + "heading": "Issue-Thread Interactions", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "request_human_input", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:258", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L258:standalone-decisions", + "heading": "Standalone Decisions", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:362", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L362: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:404", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L404:niche-workflow-pointers", + "heading": "Niche Workflow Pointers", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:414", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L414:cases", + "heading": "Cases", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:419", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L419:company-skills-workflow", + "heading": "Company Skills Workflow", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:430", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L430:routines", + "heading": "Routines", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:441", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L441: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:448", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L448:proposing-credentials-safely", + "heading": "Proposing Credentials Safely", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:455", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L455:reading-granted-secrets", + "heading": "Reading Granted Secrets", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:481", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L481:critical-rules", + "heading": "Critical Rules", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:505", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L505:comment-style-required", + "heading": "Comment Style (Required)", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/SKILL.md:537", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L537:update", + "heading": "Update", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:547", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L547: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:580", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L580: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:609", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L609:searching-issues", + "heading": "Searching Issues", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/SKILL.md:619", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/SKILL.md#L619:full-reference", + "heading": "Full Reference", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1:paperclip-api-reference", + "heading": "Paperclip API Reference", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:7", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L7:response-schemas", + "heading": "Response Schemas", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:9", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L9:agent-record-get-api-agents-me-or-get-api-agents-agentid", + "heading": "Agent Record (`GET /api/agents/me` or `GET /api/agents/:agentId`)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:42", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L42:company-portability", + "heading": "Company Portability", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:108", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L108:issue-with-ancestors-get-api-issues-issueid", + "heading": "Issue with Ancestors (`GET /api/issues/:issueId`)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:194", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L194:issue-update-response-patch-api-issues-issueid", + "heading": "Issue Update Response (`PATCH /api/issues/:issueId`)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:236", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L236:blocker-diagnostics-get-api-issues-issueid-diagnostics-blockers", + "heading": "Blocker Diagnostics (`GET /api/issues/:issueId/diagnostics/blockers`)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:275", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L275:wake-diagnostics-get-api-issues-issueid-diagnostics-wakes", + "heading": "Wake Diagnostics (`GET /api/issues/:issueId/diagnostics/wakes`)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:319", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L319:subtree-diagnostics-get-api-issues-issueid-diagnostics-subtree", + "heading": "Subtree Diagnostics (`GET /api/issues/:issueId/diagnostics/subtree`)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:367", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L367:execution-policy-fields-on-an-issue", + "heading": "Execution Policy Fields On An Issue", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:419", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L419:cross-agent-review-gates", + "heading": "Cross-Agent Review Gates", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:452", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L452:worked-example-ic-heartbeat", + "heading": "Worked Example: IC Heartbeat", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:457", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L457:1-identity-skip-if-already-in-context", + "heading": "1. Identity (skip if already in context)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:461", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L461:2-check-inbox", + "heading": "2. Check inbox", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:468", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L468:3-already-have-issue-101-in-progress-highest-priority-continue-it", + "heading": "3. Already have issue-101 in_progress (highest priority). Continue it.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:475", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L475:4-do-the-actual-work-write-code-run-tests", + "heading": "4. Do the actual work (write code, run tests)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:477", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L477:5-work-is-done-update-status-and-comment-in-one-call", + "heading": "5. Work is done. Update status and comment in one call.", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:481", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L481:6-still-have-time-checkout-the-next-task", + "heading": "6. Still have time. Checkout the next task.", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:488", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L488:7-made-partial-progress-not-done-yet-comment-and-exit", + "heading": "7. Made partial progress, not done yet. Comment and exit.", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:493", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L493:worked-example-report-a-board-user-s-mine-inbox", + "heading": "Worked Example: Report A Board User's Mine Inbox", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:498", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L498:board-user-created-the-requesting-issue", + "heading": "Board user created the requesting issue.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:502", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L502:fetch-the-board-user-s-mine-inbox-issues", + "heading": "Fetch the board user's Mine inbox issues.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:516", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L516:summarize-it-back-to-the-board-in-a-comment-or-document", + "heading": "Summarize it back to the board in a comment or document.", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "write_document", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:521", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L521:worked-example-archive-a-resolved-inbox-item", + "heading": "Worked Example: Archive A Resolved Inbox Item", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:526", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L526:the-responsible-user-s-id-is-resolved-from-the-authenticated-agent-run", + "heading": "The responsible user's id is resolved from the authenticated agent run.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:535", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L535:reverse-the-archive-if-it-was-premature-or-no-longer-desired", + "heading": "Reverse the archive if it was premature or no longer desired.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:545", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L545:worked-example-reviewer-approver-heartbeat", + "heading": "Worked Example: Reviewer / Approver Heartbeat", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:584", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L584:worked-example-manager-heartbeat", + "heading": "Worked Example: Manager Heartbeat", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:587", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L587:1-identity-skip-if-already-in-context", + "heading": "1. Identity (skip if already in context)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:591", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L591:2-check-team-status", + "heading": "2. Check team status", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:598", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L598:3-agent-42-is-blocked-read-comments", + "heading": "3. Agent-42 is blocked. Read comments.", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:602", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L602:4-unblock-reassign-and-comment", + "heading": "4. Unblock: reassign and comment.", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:606", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L606:5-check-own-assignments", + "heading": "5. Check own assignments.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:613", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L613:6-create-subtasks-and-delegate", + "heading": "6. Create subtasks and delegate.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:619", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L619:load-tests-depend-on-caching-layer-being-done-first-paperclip-will-auto-wake-agent-55-when-the-blocker-resolves", + "heading": "^ Load tests depend on caching layer being done first. Paperclip will auto-wake agent-55 when the blocker resolves.", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:624", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L624:7-dashboard-for-health-check", + "heading": "7. Dashboard for health check.", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:630", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L630:comments-and-mentions", + "heading": "Comments and @-mentions", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:637", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L637:update", + "heading": "Update", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:675", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L675:cross-team-work-and-delegation", + "heading": "Cross-Team Work and Delegation", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:679", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L679:receiving-cross-team-work", + "heading": "Receiving cross-team work", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:689", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L689:escalation", + "heading": "Escalation", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:699", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L699:company-context", + "heading": "Company Context", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:711", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L711:company-branding-ceo-board", + "heading": "Company Branding (CEO / Board)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:731", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L731:openclaw-invite-prompt-ceo", + "heading": "OpenClaw Invite Prompt (CEO)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:750", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L750:setting-agent-instructions-path", + "heading": "Setting Agent Instructions Path", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:783", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L783:project-setup-create-workspace", + "heading": "Project Setup (Create + Workspace)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:787", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L787: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", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L806: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", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L835: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", + "heading": "CEO strategy approval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:868", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L868: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:923", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L923:checkbox-confirmations", + "heading": "Checkbox confirmations", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1038", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1038: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:1148", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1148: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:1154", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1154: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:1172", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1172:issue-lifecycle", + "heading": "Issue Lifecycle", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1202", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1202:error-handling", + "heading": "Error Handling", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1216", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1216: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:1218", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1218:agents", + "heading": "Agents", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1239", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1239:issues-tasks", + "heading": "Issues (Tasks)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1279", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1279: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:1303", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1303:routines", + "heading": "Routines", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1319", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1319: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:1341", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1341:secrets", + "heading": "Secrets", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/api-reference.md:1354", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1354: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:1413", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1413: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:1452", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/api-reference.md#L1452:common-mistakes", + "heading": "Common Mistakes", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/artifacts.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/artifacts.md#L1: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/references/artifacts.md:15", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/artifacts.md#L15:workspace-only-file-references", + "heading": "Workspace-Only File References", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/cases.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L1:cases", + "heading": "Cases", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/cases.md:12", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L12:core-model", + "heading": "Core Model", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:29", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L29:upsert-semantics", + "heading": "Upsert Semantics", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:67", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L67:read-and-search", + "heading": "Read And Search", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:90", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L90:documents", + "heading": "Documents", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "write_document", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/cases.md:118", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L118:fields", + "heading": "Fields", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:151", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L151:issue-links", + "heading": "Issue Links", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:176", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L176:child-cases", + "heading": "Child Cases", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/cases.md:195", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L195:attachments", + "heading": "Attachments", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:208", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L208:lifecycle", + "heading": "Lifecycle", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/cases.md:222", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/cases.md#L222:worked-blog-post-example", + "heading": "Worked Blog Post Example", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L1:company-skills-workflow", + "heading": "Company Skills Workflow", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:5", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L5:what-exists", + "heading": "What Exists", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:22", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L22:permission-model", + "heading": "Permission Model", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:29", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L29:core-endpoints", + "heading": "Core Endpoints", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:65", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L65:install-a-skill-into-the-company", + "heading": "Install A Skill Into The Company", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:75", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L75:app-shipped-catalog", + "heading": "App-shipped catalog", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:101", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L101:external-source-import", + "heading": "External source import", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:105", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L105:source-types-in-order-of-preference", + "heading": "Source types (in order of preference)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:116", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L116:example-skills-sh-import-preferred", + "heading": "Example: skills.sh import (preferred)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:138", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L138:example-github-import", + "heading": "Example: GitHub import", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:164", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L164:inspect-what-was-installed", + "heading": "Inspect What Was Installed", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:181", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L181:assign-skills-to-an-existing-agent", + "heading": "Assign Skills To An Existing Agent", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:216", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L216:include-skills-during-hire-or-create", + "heading": "Include Skills During Hire Or Create", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/company-skills.md:256", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/company-skills.md#L256:notes", + "heading": "Notes", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L1:issue-workspace-runtime-controls", + "heading": "Issue Workspace Runtime Controls", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:5", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L5:discover-the-workspace", + "heading": "Discover the Workspace", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:23", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L23:control-services", + "heading": "Control Services", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:28", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L28:start-all-configured-services-waits-for-configured-readiness-checks", + "heading": "Start all configured services; waits for configured readiness checks.", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:36", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L36:restart-all-configured-services", + "heading": "Restart all configured services.", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:44", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L44:stop-all-running-services", + "heading": "Stop all running services.", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:63", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L63:read-the-url", + "heading": "Read the URL", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/issue-workspaces.md:72", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/issue-workspaces.md#L72:mcp-tools", + "heading": "MCP Tools", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L1:paperclip-routines", + "heading": "Paperclip Routines", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/routines.md:16", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L16:lifecycle", + "heading": "Lifecycle", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:27", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L27:creating-a-routine", + "heading": "Creating a Routine", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/routines.md:64", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L64:concurrency-policies", + "heading": "Concurrency Policies", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:76", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L76:catch-up-policies", + "heading": "Catch-Up Policies", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:87", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L87:activity-gated-scheduled-runs", + "heading": "Activity-Gated Scheduled Runs", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:107", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L107:example-skip-quiet-nights", + "heading": "Example: skip quiet nights", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:126", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L126:adding-triggers", + "heading": "Adding Triggers", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:136", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L136:schedule-cron", + "heading": "Schedule (cron)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:150", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L150:webhook", + "heading": "Webhook", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:167", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L167:api-manual-only", + "heading": "API (manual only)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:179", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L179:updating-and-deleting-triggers", + "heading": "Updating and Deleting Triggers", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:196", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L196:manual-run", + "heading": "Manual Run", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/routines.md:212", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L212:updating-a-routine", + "heading": "Updating a Routine", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/routines.md:223", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/routines.md#L223:reading-routines-and-runs", + "heading": "Reading Routines and Runs", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:1", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L1:paperclip-workflow-playbooks", + "heading": "Paperclip Workflow Playbooks", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:7", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L7:project-setup-ceo-manager", + "heading": "Project Setup (CEO/Manager)", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:22", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L22:openclaw-invite-ceo", + "heading": "OpenClaw Invite (CEO)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:50", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L50:setting-agent-instructions-path", + "heading": "Setting Agent Instructions Path", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:79", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L79:company-import-export", + "heading": "Company Import / Export", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "scoped_discovery", + "expectedMockState": "operation_result" + }, + { + "id": "skill:skills/paperclip/references/workflows.md:106", + "kind": "skill_heading", + "sourceAnchor": "skills/paperclip/references/workflows.md#L106:self-test-playbook-app-level", + "heading": "Self-Test Playbook (App-Level)", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_reconciliation", + "expectedMockState": "runtime_decision_record" + } + ] +} diff --git a/packages/paperclip-runner/generated/phase-07/capability-contract.md b/packages/paperclip-runner/generated/phase-07/capability-contract.md new file mode 100644 index 00000000000..f8f97ee48cb --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/capability-contract.md @@ -0,0 +1,10 @@ +# Phase 7 Capability Contract + +Generated by `scripts/generate-phase-07-contract.mjs`; do not edit generated files. + +- Skill/reference headings: 152 +- Legacy MCP tools: 41 +- Eval cases: 106 across 16 groups +- Deterministic content SHA-256: `667d410acdfd7ae42f60eee242c14f9fe69c454a5e1fa6efb5b6d38f6b338972` + +Every row has exactly one primary disposition, a source anchor, a semantic operation, and a mock-state expectation. diff --git a/packages/paperclip-runner/generated/phase-07/downstream-handoff.md b/packages/paperclip-runner/generated/phase-07/downstream-handoff.md new file mode 100644 index 00000000000..98e2e886c27 --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/downstream-handoff.md @@ -0,0 +1,20 @@ +# Phase 7A Downstream Handoff + +Generated by `scripts/generate-phase-07-contract.mjs`; do not edit generated files. + +## Stable Inputs + +- `capabilities.yaml`: every current Paperclip skill and reference heading, including its source anchor, disposition, semantic operation, and mock-state expectation. +- `mcp-tool-map.yaml`: the complete 41-tool legacy MCP replacement map. +- `eval-traceability.yaml`: all 106 corpus cases in 16 groups, including fixtures, grants, operations, state projections, forbids, and browser evidence IDs. +- `contract-schema.json`: required row fields and the closed disposition enum. + +## Consumer Tracks + +- **7B UX interaction map:** use eval `browserEvidenceRecipe`, semantic operation, and expected state to define transcript, authorization, and parity views. +- **7C mock control plane:** implement only the state projections and control-plane-owned operations represented by the generated rows. +- **7D semantic catalog:** use the operation/disposition fields to create always and optional descriptors; control-plane-owned rows stay absent from model tools. +- **7E eval conformance:** import each case by stable ID and assert the declared operation, forbidden operation set, and final mock projection. +- **7F scenario explorer:** index scenarios by the generated evidence recipe and render the linked source anchor, disposition, operation, and mock-state projection. + +`pnpm --dir packages/paperclip-runner check:phase-07-contract` is the drift gate before consuming these artifacts. diff --git a/packages/paperclip-runner/generated/phase-07/eval-traceability.yaml b/packages/paperclip-runner/generated/phase-07/eval-traceability.yaml new file mode 100644 index 00000000000..5eafdde5f43 --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/eval-traceability.yaml @@ -0,0 +1,1935 @@ +{ + "schemaVersion": 1, + "rows": [ + { + "id": "hb-context-01", + "title": "Prefer compact heartbeat context", + "group": "hb", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/hb.yaml#hb-context-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "hb-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "get_task_context", + "expectedSemanticOperations": [ + "get_task_context" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "active_task_context", + "browserEvidenceRecipe": "hb/hb-context-01" + }, + { + "id": "hb-inbox-lite-01", + "title": "Use compact inbox for normal heartbeat", + "group": "hb", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/hb.yaml#hb-inbox-lite-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "hb-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "get_task_context", + "expectedSemanticOperations": [ + "get_task_context" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "active_task_context", + "browserEvidenceRecipe": "hb/hb-inbox-lite-01" + }, + { + "id": "hb-pick-priority-01", + "title": "Prioritize in-progress work", + "group": "hb", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/hb.yaml#hb-pick-priority-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "hb-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "get_task_context", + "expectedSemanticOperations": [ + "get_task_context" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "active_task_context", + "browserEvidenceRecipe": "hb/hb-pick-priority-01" + }, + { + "id": "hb-scoped-wake-01", + "title": "Scoped wake skips inbox", + "group": "hb", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/hb.yaml#hb-scoped-wake-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "hb-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "get_task_context", + "expectedSemanticOperations": [ + "get_task_context" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "active_task_context", + "browserEvidenceRecipe": "hb/hb-scoped-wake-01" + }, + { + "id": "hb-wake-comment-01", + "title": "Read triggering comment first", + "group": "hb", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/hb.yaml#hb-wake-comment-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "hb-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "get_task_context", + "expectedSemanticOperations": [ + "get_task_context" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "active_task_context", + "browserEvidenceRecipe": "hb/hb-wake-comment-01" + }, + { + "id": "co-409-stop-01", + "title": "Stop after checkout conflict", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-409-stop-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-409-stop-01" + }, + { + "id": "co-409-stop-02", + "title": "Named conflict remains terminal", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-409-stop-02", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-409-stop-02" + }, + { + "id": "co-before-work-01", + "title": "Checkout before issue writes", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-before-work-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-before-work-01" + }, + { + "id": "co-body-contract-01", + "title": "Send checkout request contract", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-body-contract-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-body-contract-01" + }, + { + "id": "co-no-status-patch-01", + "title": "Do not patch into progress", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-no-status-patch-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-no-status-patch-01" + }, + { + "id": "co-runid-header-01", + "title": "Audit modifying calls", + "group": "co", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/co.yaml#co-runid-header-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "co-baseline", + "dominantValidationKind": "control_plane_invariant", + "requiredCapabilityGrants": [], + "semanticOperation": "atomic_checkout", + "expectedSemanticOperations": [ + "atomic_checkout" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "checkout_decision", + "browserEvidenceRecipe": "co/co-runid-header-01" + }, + { + "id": "st-backlog-park-01", + "title": "Park postponed work in backlog", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-backlog-park-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-backlog-park-01" + }, + { + "id": "st-blocked-owner-01", + "title": "Record blocker owner", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-blocked-owner-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-blocked-owner-01" + }, + { + "id": "st-crossteam-cancel-01", + "title": "Reassign cross-team task instead of cancelling", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-crossteam-cancel-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-crossteam-cancel-01" + }, + { + "id": "st-done-comment-01", + "title": "Close task with explanatory comment", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-done-comment-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-done-comment-01" + }, + { + "id": "st-env-blocked-notdone-01", + "title": "Block impossible deliverable with an owner", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-env-blocked-notdone-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-env-blocked-notdone-01" + }, + { + "id": "st-ephemeral-verify-01", + "title": "Disclose ephemeral verification", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-ephemeral-verify-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-ephemeral-verify-01" + }, + { + "id": "st-handback-review-01", + "title": "Return task for board review", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-handback-review-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-handback-review-01" + }, + { + "id": "st-unverified-toolchain-01", + "title": "Block when verification cannot run", + "group": "st", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/st.yaml#st-unverified-toolchain-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "st-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "semantic_task_disposition", + "expectedSemanticOperations": [ + "semantic_task_disposition" + ], + "forbiddenOperations": [], + "expectedMockState": "terminal_or_blocked_task", + "browserEvidenceRecipe": "st/st-unverified-toolchain-01" + }, + { + "id": "cm-mention-structured-01", + "title": "Render structured mentions", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-mention-structured-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-mention-structured-01" + }, + { + "id": "cm-multiline-01", + "title": "Preserve multiline comments", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-multiline-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-multiline-01" + }, + { + "id": "cm-prefixed-url-01", + "title": "Prefix internal URLs by company", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-prefixed-url-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-prefixed-url-01" + }, + { + "id": "cm-progress-nextaction-01", + "title": "Report progress and next owner", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-progress-nextaction-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-progress-nextaction-01" + }, + { + "id": "cm-runid-modify-01", + "title": "Audit comment writes", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-runid-modify-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-runid-modify-01" + }, + { + "id": "cm-ticket-link-01", + "title": "Render ticket links", + "group": "cm", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/cm.yaml#cm-ticket-link-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "cm-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "report_progress", + "expectedSemanticOperations": [ + "report_progress" + ], + "forbiddenOperations": [], + "expectedMockState": "rendered_comment", + "browserEvidenceRecipe": "cm/cm-ticket-link-01" + }, + { + "id": "se-get-issue-01", + "title": "Read issue and history", + "group": "se", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/se.yaml#se-get-issue-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "se-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "se:read_or_write" + ], + "semanticOperation": "search_tasks", + "expectedSemanticOperations": [ + "search_tasks" + ], + "forbiddenOperations": [], + "expectedMockState": "company_scoped_search", + "browserEvidenceRecipe": "se/se-get-issue-01" + }, + { + "id": "se-q-comments-01", + "title": "Search comment bodies", + "group": "se", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/se.yaml#se-q-comments-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "se-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "se:read_or_write" + ], + "semanticOperation": "search_tasks", + "expectedSemanticOperations": [ + "search_tasks" + ], + "forbiddenOperations": [], + "expectedMockState": "company_scoped_search", + "browserEvidenceRecipe": "se/se-q-comments-01" + }, + { + "id": "se-q-filters-01", + "title": "Combine search filters", + "group": "se", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/se.yaml#se-q-filters-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "se-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "se:read_or_write" + ], + "semanticOperation": "search_tasks", + "expectedSemanticOperations": [ + "search_tasks" + ], + "forbiddenOperations": [], + "expectedMockState": "company_scoped_search", + "browserEvidenceRecipe": "se/se-q-filters-01" + }, + { + "id": "se-q-topic-01", + "title": "Find issue by topic", + "group": "se", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/se.yaml#se-q-topic-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "se-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "se:read_or_write" + ], + "semanticOperation": "search_tasks", + "expectedSemanticOperations": [ + "search_tasks" + ], + "forbiddenOperations": [], + "expectedMockState": "company_scoped_search", + "browserEvidenceRecipe": "se/se-q-topic-01" + }, + { + "id": "su-crossteam-billing-01", + "title": "Set cross-team billing", + "group": "su", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/su.yaml#su-crossteam-billing-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "su-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [ + "su:read_or_write" + ], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "delegation_and_wake", + "browserEvidenceRecipe": "su/su-crossteam-billing-01" + }, + { + "id": "su-inherit-workspace-01", + "title": "Inherit workspace for follow-up", + "group": "su", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/su.yaml#su-inherit-workspace-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "su-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [ + "su:read_or_write" + ], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "delegation_and_wake", + "browserEvidenceRecipe": "su/su-inherit-workspace-01" + }, + { + "id": "su-no-poll-01", + "title": "Delegate without polling", + "group": "su", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/su.yaml#su-no-poll-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "su-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [ + "su:read_or_write" + ], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "delegation_and_wake", + "browserEvidenceRecipe": "su/su-no-poll-01" + }, + { + "id": "su-parent-goal-01", + "title": "Create subtasks with parent and goal", + "group": "su", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/su.yaml#su-parent-goal-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "su-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [ + "su:read_or_write" + ], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "delegation_and_wake", + "browserEvidenceRecipe": "su/su-parent-goal-01" + }, + { + "id": "bl-cancelled-not-resolved-01", + "title": "Cancelled blocker remains unresolved", + "group": "bl", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/bl.yaml#bl-cancelled-not-resolved-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "bl-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "block_task", + "expectedSemanticOperations": [ + "block_task" + ], + "forbiddenOperations": [], + "expectedMockState": "blocker_projection", + "browserEvidenceRecipe": "bl/bl-cancelled-not-resolved-01" + }, + { + "id": "bl-clear-01", + "title": "Clear blocker set", + "group": "bl", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/bl.yaml#bl-clear-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "bl-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "block_task", + "expectedSemanticOperations": [ + "block_task" + ], + "forbiddenOperations": [], + "expectedMockState": "blocker_projection", + "browserEvidenceRecipe": "bl/bl-clear-01" + }, + { + "id": "bl-create-blocked-01", + "title": "Create dependent work blocked", + "group": "bl", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/bl.yaml#bl-create-blocked-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "bl-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "block_task", + "expectedSemanticOperations": [ + "block_task" + ], + "forbiddenOperations": [], + "expectedMockState": "blocker_projection", + "browserEvidenceRecipe": "bl/bl-create-blocked-01" + }, + { + "id": "bl-firstclass-01", + "title": "Use first-class dependencies", + "group": "bl", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/bl.yaml#bl-firstclass-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "bl-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "block_task", + "expectedSemanticOperations": [ + "block_task" + ], + "forbiddenOperations": [], + "expectedMockState": "blocker_projection", + "browserEvidenceRecipe": "bl/bl-firstclass-01" + }, + { + "id": "bl-read-owners-01", + "title": "Read blocker owners", + "group": "bl", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/bl.yaml#bl-read-owners-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "bl-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "block_task", + "expectedSemanticOperations": [ + "block_task" + ], + "forbiddenOperations": [], + "expectedMockState": "blocker_projection", + "browserEvidenceRecipe": "bl/bl-read-owners-01" + }, + { + "id": "dp-base-revision-01", + "title": "Use latest document revision", + "group": "dp", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/dp.yaml#dp-base-revision-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "dp-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "write_document", + "expectedSemanticOperations": [ + "write_document" + ], + "forbiddenOperations": [], + "expectedMockState": "revision_safe_document", + "browserEvidenceRecipe": "dp/dp-base-revision-01" + }, + { + "id": "dp-plan-doc-01", + "title": "Store plan in plan document", + "group": "dp", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/dp.yaml#dp-plan-doc-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "dp-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "write_document", + "expectedSemanticOperations": [ + "write_document" + ], + "forbiddenOperations": [], + "expectedMockState": "revision_safe_document", + "browserEvidenceRecipe": "dp/dp-plan-doc-01" + }, + { + "id": "dp-plan-link-comment-01", + "title": "Deep-link plan document", + "group": "dp", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/dp.yaml#dp-plan-link-comment-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "dp-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "write_document", + "expectedSemanticOperations": [ + "write_document" + ], + "forbiddenOperations": [], + "expectedMockState": "revision_safe_document", + "browserEvidenceRecipe": "dp/dp-plan-link-comment-01" + }, + { + "id": "ix-checkbox-01", + "title": "Use checkbox confirmation for subset", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-checkbox-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-checkbox-01" + }, + { + "id": "ix-checkbox-result-01", + "title": "Act on selected option IDs", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-checkbox-result-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-checkbox-result-01" + }, + { + "id": "ix-confirmation-plan-01", + "title": "Bind plan confirmation to revision", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-confirmation-plan-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-confirmation-plan-01" + }, + { + "id": "ix-continuation-01", + "title": "Use continuation wake policy", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-continuation-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-continuation-01" + }, + { + "id": "ix-questions-01", + "title": "Use typed questions", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-questions-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-questions-01" + }, + { + "id": "ix-stale-target-01", + "title": "Rebuild after stale target", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-stale-target-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-stale-target-01" + }, + { + "id": "ix-suggest-tasks-01", + "title": "Suggest before creating tasks", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-suggest-tasks-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-suggest-tasks-01" + }, + { + "id": "ix-superseded-comment-01", + "title": "Address superseding comment", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-superseded-comment-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-superseded-comment-01" + }, + { + "id": "ix-verdicts-01", + "title": "Request item verdicts", + "group": "ix", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ix.yaml#ix-verdicts-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ix-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "request_human_input", + "expectedSemanticOperations": [ + "request_human_input" + ], + "forbiddenOperations": [], + "expectedMockState": "interaction_and_wake", + "browserEvidenceRecipe": "ix/ix-verdicts-01" + }, + { + "id": "ap-approval-deny-01", + "title": "Keep denied approval task open", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-approval-deny-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-approval-deny-01" + }, + { + "id": "ap-approval-wake-01", + "title": "Resolve approval wake", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-approval-wake-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-approval-wake-01" + }, + { + "id": "ap-board-approval-01", + "title": "Request board approval for spend", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-board-approval-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-board-approval-01" + }, + { + "id": "ap-mcp-expiry-01", + "title": "Re-call once after approval expiry", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-mcp-expiry-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-mcp-expiry-01" + }, + { + "id": "ap-mcp-gate-01", + "title": "Wait on pending MCP approval", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-mcp-gate-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-mcp-gate-01" + }, + { + "id": "ap-mcp-pathmissing-01", + "title": "Reroute on missing approval path", + "group": "ap", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ap.yaml#ap-mcp-pathmissing-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "ap-baseline", + "dominantValidationKind": "authorization_policy", + "requiredCapabilityGrants": [ + "ap:read_or_write" + ], + "semanticOperation": "request_approval", + "expectedSemanticOperations": [ + "request_approval" + ], + "forbiddenOperations": [], + "expectedMockState": "approval_gate", + "browserEvidenceRecipe": "ap/ap-mcp-pathmissing-01" + }, + { + "id": "ar-no-done-without-upload-01", + "title": "Upload file deliverable before close", + "group": "ar", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ar.yaml#ar-no-done-without-upload-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ar-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "register_deliverable", + "expectedSemanticOperations": [ + "register_deliverable" + ], + "forbiddenOperations": [], + "expectedMockState": "artifact_and_work_product", + "browserEvidenceRecipe": "ar/ar-no-done-without-upload-01" + }, + { + "id": "ar-upload-before-done-01", + "title": "Attach deliverable before done", + "group": "ar", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ar.yaml#ar-upload-before-done-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ar-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "register_deliverable", + "expectedSemanticOperations": [ + "register_deliverable" + ], + "forbiddenOperations": [], + "expectedMockState": "artifact_and_work_product", + "browserEvidenceRecipe": "ar/ar-upload-before-done-01" + }, + { + "id": "ar-workproduct-pr-01", + "title": "Register pull request work product", + "group": "ar", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ar.yaml#ar-workproduct-pr-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ar-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "register_deliverable", + "expectedSemanticOperations": [ + "register_deliverable" + ], + "forbiddenOperations": [], + "expectedMockState": "artifact_and_work_product", + "browserEvidenceRecipe": "ar/ar-workproduct-pr-01" + }, + { + "id": "ar-workproduct-wsfile-01", + "title": "Register workspace file work product", + "group": "ar", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/ar.yaml#ar-workproduct-wsfile-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "ar-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [], + "semanticOperation": "register_deliverable", + "expectedSemanticOperations": [ + "register_deliverable" + ], + "forbiddenOperations": [], + "expectedMockState": "artifact_and_work_product", + "browserEvidenceRecipe": "ar/ar-workproduct-wsfile-01" + }, + { + "id": "er-blocked-dedup-01", + "title": "Avoid duplicate blocked update", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-blocked-dedup-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-blocked-dedup-01" + }, + { + "id": "er-budget-critical-01", + "title": "Choose critical work at high budget", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-budget-critical-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-budget-critical-01" + }, + { + "id": "er-close-retry-fault-01", + "title": "Retry terminal close faults", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-close-retry-fault-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-close-retry-fault-01" + }, + { + "id": "er-exec-not-participant-01", + "title": "Do not advance foreign review stage", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-exec-not-participant-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-exec-not-participant-01" + }, + { + "id": "er-exec-participant-01", + "title": "Advance own review stage", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-exec-participant-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-exec-participant-01" + }, + { + "id": "er-mention-handoff-01", + "title": "Checkout explicit mention handoff", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-mention-handoff-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-mention-handoff-01" + }, + { + "id": "er-mention-noassign-01", + "title": "Do not claim FYI mention", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-mention-noassign-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-mention-noassign-01" + }, + { + "id": "er-no-unassigned-01", + "title": "Exit on empty inbox", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-no-unassigned-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-no-unassigned-01" + }, + { + "id": "er-release-01", + "title": "Release task handback", + "group": "er", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/er.yaml#er-release-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "er-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "runtime_reconciliation", + "expectedSemanticOperations": [ + "runtime_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "recovery_or_restraint", + "browserEvidenceRecipe": "er/er-release-01" + }, + { + "id": "rf-api-404-report-01", + "title": "Report missing issue honestly", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-api-404-report-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-api-404-report-01" + }, + { + "id": "rf-api-cancel-obsolete-01", + "title": "Cancel obsolete work", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-api-cancel-obsolete-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-api-cancel-obsolete-01" + }, + { + "id": "rf-api-mention-discipline-01", + "title": "Avoid non-actionable mentions", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-api-mention-discipline-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-api-mention-discipline-01" + }, + { + "id": "rf-api-mgr-heartbeat-01", + "title": "Run manager heartbeat", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-api-mgr-heartbeat-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-api-mgr-heartbeat-01" + }, + { + "id": "rf-api-review-changes-01", + "title": "Request review changes", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-api-review-changes-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-api-review-changes-01" + }, + { + "id": "rf-art-attachment-wp-01", + "title": "Register attachment work product", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-art-attachment-wp-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-art-attachment-wp-01" + }, + { + "id": "rf-case-child-01", + "title": "Create child case", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-case-child-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-case-child-01" + }, + { + "id": "rf-case-lifecycle-link-01", + "title": "Link active case", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-case-lifecycle-link-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-case-lifecycle-link-01" + }, + { + "id": "rf-case-upsert-doc-01", + "title": "Upsert case document", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-case-upsert-doc-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-case-upsert-doc-01" + }, + { + "id": "rf-cskill-audit-01", + "title": "Audit library and attached skills", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-cskill-audit-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-cskill-audit-01" + }, + { + "id": "rf-cskill-install-attach-01", + "title": "Install then attach skill", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-cskill-install-attach-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-cskill-install-attach-01" + }, + { + "id": "rf-cskill-self-sync-01", + "title": "Preserve skills during sync", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-cskill-self-sync-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-cskill-self-sync-01" + }, + { + "id": "rf-iws-start-url-01", + "title": "Start issue workspace service", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-iws-start-url-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-iws-start-url-01" + }, + { + "id": "rf-iws-target-restart-01", + "title": "Restart selected workspace service", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-iws-target-restart-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-iws-target-restart-01" + }, + { + "id": "rf-routine-create-01", + "title": "Create weekly routine", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-routine-create-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-routine-create-01" + }, + { + "id": "rf-routine-manual-run-01", + "title": "Manually run routine", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-routine-manual-run-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-routine-manual-run-01" + }, + { + "id": "rf-routine-pause-01", + "title": "Pause routine reversibly", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-routine-pause-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-routine-pause-01" + }, + { + "id": "rf-routine-policy-01", + "title": "Configure routine policies", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-routine-policy-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-routine-policy-01" + }, + { + "id": "rf-routine-webhook-01", + "title": "Add signed webhook trigger", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-routine-webhook-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-routine-webhook-01" + }, + { + "id": "rf-wf-export-preview-01", + "title": "Preview narrowed export", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-wf-export-preview-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-wf-export-preview-01" + }, + { + "id": "rf-wf-invite-01", + "title": "Create paste-ready invite", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-wf-invite-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-wf-invite-01" + }, + { + "id": "rf-wf-project-setup-01", + "title": "Set up repo workspace project", + "group": "rf", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rf.yaml#rf-wf-project-setup-01", + "primaryDisposition": "optional_agent_tool", + "fixtureProfile": "rf-baseline", + "dominantValidationKind": "agent_tool_contract", + "requiredCapabilityGrants": [ + "rf:read_or_write" + ], + "semanticOperation": "optional_reference_workflow", + "expectedSemanticOperations": [ + "optional_reference_workflow" + ], + "forbiddenOperations": [], + "expectedMockState": "scoped_reference_result", + "browserEvidenceRecipe": "rf/rf-wf-project-setup-01" + }, + { + "id": "mh-blocked-handoff-01", + "title": "Create review handoff blocker", + "group": "mh", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/mh.yaml#mh-blocked-handoff-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "mh-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "multi_hop_projection", + "browserEvidenceRecipe": "mh/mh-blocked-handoff-01" + }, + { + "id": "mh-children-complete-01", + "title": "Complete parent after children", + "group": "mh", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/mh.yaml#mh-children-complete-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "mh-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "multi_hop_projection", + "browserEvidenceRecipe": "mh/mh-children-complete-01" + }, + { + "id": "mh-plan-confirm-01", + "title": "Confirm a written plan", + "group": "mh", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/mh.yaml#mh-plan-confirm-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "mh-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "multi_hop_projection", + "browserEvidenceRecipe": "mh/mh-plan-confirm-01" + }, + { + "id": "mh-subtask-tree-01", + "title": "Create sequenced subtask tree", + "group": "mh", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/mh.yaml#mh-subtask-tree-01", + "primaryDisposition": "always_agent_tool", + "fixtureProfile": "mh-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "create_child_task", + "expectedSemanticOperations": [ + "create_child_task" + ], + "forbiddenOperations": [], + "expectedMockState": "multi_hop_projection", + "browserEvidenceRecipe": "mh/mh-subtask-tree-01" + }, + { + "id": "rs-dependency-blocked-wake-01", + "title": "Triages dependency-blocked wake", + "group": "rs", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rs.yaml#rs-dependency-blocked-wake-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "rs-baseline", + "dominantValidationKind": "restraint_no_call", + "requiredCapabilityGrants": [], + "semanticOperation": "none", + "expectedSemanticOperations": [], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "unchanged_state", + "browserEvidenceRecipe": "rs/rs-dependency-blocked-wake-01" + }, + { + "id": "rs-question-only-01", + "title": "Answer question without mutation", + "group": "rs", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rs.yaml#rs-question-only-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "rs-baseline", + "dominantValidationKind": "restraint_no_call", + "requiredCapabilityGrants": [], + "semanticOperation": "none", + "expectedSemanticOperations": [], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "unchanged_state", + "browserEvidenceRecipe": "rs/rs-question-only-01" + }, + { + "id": "rs-secret-hygiene-01", + "title": "Never expose API key", + "group": "rs", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/rs.yaml#rs-secret-hygiene-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "rs-baseline", + "dominantValidationKind": "restraint_no_call", + "requiredCapabilityGrants": [], + "semanticOperation": "none", + "expectedSemanticOperations": [], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "unchanged_state", + "browserEvidenceRecipe": "rs/rs-secret-hygiene-01" + }, + { + "id": "wk-ask-mode-01", + "title": "Answer-only ask-mode wake", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-ask-mode-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-ask-mode-01" + }, + { + "id": "wk-plan-accepted-01", + "title": "Create tasks after accepted plan", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-plan-accepted-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-plan-accepted-01" + }, + { + "id": "wk-plan-annotation-01", + "title": "Revise annotated plan", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-plan-annotation-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-plan-annotation-01" + }, + { + "id": "wk-plan-directive-01", + "title": "Write plan then request confirmation", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-plan-directive-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-plan-directive-01" + }, + { + "id": "wk-plan-directive-02", + "title": "Honor prose planning directive", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-plan-directive-02", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-plan-directive-02" + }, + { + "id": "wk-recovery-processlost-01", + "title": "Recover without duplicate work", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-recovery-processlost-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-recovery-processlost-01" + }, + { + "id": "wk-resume-delta-01", + "title": "Apply condensed resume contract", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-resume-delta-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-resume-delta-01" + }, + { + "id": "wk-skilltest-mode-01", + "title": "Write structured skill test output", + "group": "wk", + "sourceAnchor": "paperclip-evals/paperclip-skill-optimization/wk.yaml#wk-skilltest-mode-01", + "primaryDisposition": "control_plane_owned", + "fixtureProfile": "wk-baseline", + "dominantValidationKind": "combined_multi_hop", + "requiredCapabilityGrants": [], + "semanticOperation": "wake_reconciliation", + "expectedSemanticOperations": [ + "wake_reconciliation" + ], + "forbiddenOperations": [ + "legacy_mcp_transport" + ], + "expectedMockState": "wake_mode_projection", + "browserEvidenceRecipe": "wk/wk-skilltest-mode-01" + } + ] +} diff --git a/packages/paperclip-runner/generated/phase-07/mcp-tool-map.yaml b/packages/paperclip-runner/generated/phase-07/mcp-tool-map.yaml new file mode 100644 index 00000000000..d7dbe60b8f4 --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/mcp-tool-map.yaml @@ -0,0 +1,374 @@ +{ + "schemaVersion": 1, + "rows": [ + { + "id": "mcp:paperclipMe", + "kind": "legacy_mcp_tool", + "name": "paperclipMe", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L239:paperclipme", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "injected_actor_context", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "mcp:paperclipInboxLite", + "kind": "legacy_mcp_tool", + "name": "paperclipInboxLite", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L245:paperclipinboxlite", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runner_work_selection", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "mcp:paperclipListAgents", + "kind": "legacy_mcp_tool", + "name": "paperclipListAgents", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L251:papercliplistagents", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "list_agents", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetAgent", + "kind": "legacy_mcp_tool", + "name": "paperclipGetAgent", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L257:paperclipgetagent", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_agent", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListIssues", + "kind": "legacy_mcp_tool", + "name": "paperclipListIssues", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L266:papercliplistissues", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "search_tasks", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetIssue", + "kind": "legacy_mcp_tool", + "name": "paperclipGetIssue", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L281:paperclipgetissue", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "get_task_context", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetHeartbeatContext", + "kind": "legacy_mcp_tool", + "name": "paperclipGetHeartbeatContext", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L287:paperclipgetheartbeatcontext", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "get_task_context", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListComments", + "kind": "legacy_mcp_tool", + "name": "paperclipListComments", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L296:papercliplistcomments", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "get_task_history", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetComment", + "kind": "legacy_mcp_tool", + "name": "paperclipGetComment", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L309:paperclipgetcomment", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "get_task_history", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListIssueApprovals", + "kind": "legacy_mcp_tool", + "name": "paperclipListIssueApprovals", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L316:papercliplistissueapprovals", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "get_task_context", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListDocuments", + "kind": "legacy_mcp_tool", + "name": "paperclipListDocuments", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L322:papercliplistdocuments", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "list_documents", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetDocument", + "kind": "legacy_mcp_tool", + "name": "paperclipGetDocument", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L328:paperclipgetdocument", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "read_document", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListDocumentRevisions", + "kind": "legacy_mcp_tool", + "name": "paperclipListDocumentRevisions", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L335:papercliplistdocumentrevisions", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "list_document_revisions", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListProjects", + "kind": "legacy_mcp_tool", + "name": "paperclipListProjects", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L345:papercliplistprojects", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "list_projects", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetProject", + "kind": "legacy_mcp_tool", + "name": "paperclipGetProject", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L351:paperclipgetproject", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_project", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetIssueWorkspaceRuntime", + "kind": "legacy_mcp_tool", + "name": "paperclipGetIssueWorkspaceRuntime", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L360:paperclipgetissueworkspaceruntime", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_workspace_runtime", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipControlIssueWorkspaceServices", + "kind": "legacy_mcp_tool", + "name": "paperclipControlIssueWorkspaceServices", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L366:paperclipcontrolissueworkspaceservices", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "control_workspace_service", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipWaitForIssueWorkspaceService", + "kind": "legacy_mcp_tool", + "name": "paperclipWaitForIssueWorkspaceService", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L383:paperclipwaitforissueworkspaceservice", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "wait_for_workspace_service", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListGoals", + "kind": "legacy_mcp_tool", + "name": "paperclipListGoals", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L409:papercliplistgoals", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "list_goals", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetGoal", + "kind": "legacy_mcp_tool", + "name": "paperclipGetGoal", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L415:paperclipgetgoal", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_goal", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListApprovals", + "kind": "legacy_mcp_tool", + "name": "paperclipListApprovals", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L421:papercliplistapprovals", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "list_approvals", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipCreateApproval", + "kind": "legacy_mcp_tool", + "name": "paperclipCreateApproval", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L430:paperclipcreateapproval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "request_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetApproval", + "kind": "legacy_mcp_tool", + "name": "paperclipGetApproval", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L439:paperclipgetapproval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipGetApprovalIssues", + "kind": "legacy_mcp_tool", + "name": "paperclipGetApprovalIssues", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L445:paperclipgetapprovalissues", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_approval_context", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipListApprovalComments", + "kind": "legacy_mcp_tool", + "name": "paperclipListApprovalComments", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L451:papercliplistapprovalcomments", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "get_approval_context", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipCreateIssue", + "kind": "legacy_mcp_tool", + "name": "paperclipCreateIssue", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L457:paperclipcreateissue", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "create_task", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipUpdateIssue", + "kind": "legacy_mcp_tool", + "name": "paperclipUpdateIssue", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L464:paperclipupdateissue", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "semantic_task_disposition", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipCheckoutIssue", + "kind": "legacy_mcp_tool", + "name": "paperclipCheckoutIssue", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L471:paperclipcheckoutissue", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "atomic_checkout", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "mcp:paperclipReleaseIssue", + "kind": "legacy_mcp_tool", + "name": "paperclipReleaseIssue", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L483:paperclipreleaseissue", + "primaryDisposition": "control_plane_owned", + "semanticOperation": "runtime_release", + "expectedMockState": "runtime_decision_record" + }, + { + "id": "mcp:paperclipAddComment", + "kind": "legacy_mcp_tool", + "name": "paperclipAddComment", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L489:paperclipaddcomment", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "report_progress", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipSuggestTasks", + "kind": "legacy_mcp_tool", + "name": "paperclipSuggestTasks", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L496:paperclipsuggesttasks", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "request_human_input", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipAskUserQuestions", + "kind": "legacy_mcp_tool", + "name": "paperclipAskUserQuestions", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L508:paperclipaskuserquestions", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "request_human_input", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipRequestConfirmation", + "kind": "legacy_mcp_tool", + "name": "paperclipRequestConfirmation", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L520:papercliprequestconfirmation", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "request_human_input", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipRequestCheckboxConfirmation", + "kind": "legacy_mcp_tool", + "name": "paperclipRequestCheckboxConfirmation", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L532:papercliprequestcheckboxconfirmation", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "request_human_input", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipUpsertIssueDocument", + "kind": "legacy_mcp_tool", + "name": "paperclipUpsertIssueDocument", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L544:paperclipupsertissuedocument", + "primaryDisposition": "always_agent_tool", + "semanticOperation": "write_document", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipRestoreIssueDocumentRevision", + "kind": "legacy_mcp_tool", + "name": "paperclipRestoreIssueDocumentRevision", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L555:papercliprestoreissuedocumentrevision", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "restore_document_revision", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipLinkIssueApproval", + "kind": "legacy_mcp_tool", + "name": "paperclipLinkIssueApproval", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L570:papercliplinkissueapproval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "link_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipUnlinkIssueApproval", + "kind": "legacy_mcp_tool", + "name": "paperclipUnlinkIssueApproval", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L579:paperclipunlinkissueapproval", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "unlink_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipApprovalDecision", + "kind": "legacy_mcp_tool", + "name": "paperclipApprovalDecision", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L589:paperclipapprovaldecision", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "decide_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipAddApprovalComment", + "kind": "legacy_mcp_tool", + "name": "paperclipAddApprovalComment", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L611:paperclipaddapprovalcomment", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "comment_on_approval", + "expectedMockState": "operation_result" + }, + { + "id": "mcp:paperclipApiRequest", + "kind": "legacy_mcp_tool", + "name": "paperclipApiRequest", + "sourceAnchor": "packages/mcp-server/src/tools.ts#L620:paperclipapirequest", + "primaryDisposition": "optional_agent_tool", + "semanticOperation": "test_only_api_escape_hatch", + "expectedMockState": "operation_result" + } + ] +} diff --git a/packages/paperclip-runner/generated/phase-07/semantic-tool-contracts.json b/packages/paperclip-runner/generated/phase-07/semantic-tool-contracts.json new file mode 100644 index 00000000000..73e26437fb8 --- /dev/null +++ b/packages/paperclip-runner/generated/phase-07/semantic-tool-contracts.json @@ -0,0 +1 @@ +[{"annotations":{"exposure":"always","operationId":"get_task_context","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the active mock task, actor, wake, ancestors, budget, and interaction results.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_task_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"get_task_history","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded comments on the active mock task.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":[],"type":"object"},"name":"get_task_history","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_documents","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List revisioned documents on the active mock task.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_documents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"read_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the current revision of one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"name":"read_document","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_document_revisions","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded revision history for one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":["key"],"type":"object"},"name":"list_document_revisions","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"report_progress","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append a durable progress comment to the active mock task.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Multiline progress update.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"report_progress","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"answer_status_question","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append the answer to a status-only wake without changing task disposition.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Concise status answer.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"answer_status_question","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"write_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Create or update an active-task document with optimistic revision safety.","inputSchema":{"additionalProperties":false,"properties":{"baseRevisionId":{"description":"Current revision id, or null when creating.","maxLength":20000,"type":["string","null"]},"body":{"description":"Markdown document body.","maxLength":200000,"minLength":1,"type":"string"},"changeSummary":{"description":"Optional revision summary.","maxLength":20000,"type":["string","null"]},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"title":{"description":"Document title.","maxLength":300,"minLength":1,"type":"string"}},"required":["idempotencyKey","key","title","body","baseRevisionId"],"type":"object"},"name":"write_document","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"request_human_input","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Create a typed, durable interaction on the active mock task.","inputSchema":{"additionalProperties":false,"properties":{"continuationPolicy":{"enum":["none","wake_assignee","wake_assignee_on_accept"]},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"interactionKind":{"enum":["confirmation","checkbox","questions","suggest_tasks","item_verdicts"]},"payload":{"additionalProperties":true,"type":"object"},"prompt":{"description":"Question or decision prompt.","maxLength":10000,"minLength":1,"type":"string"},"targetRevisionId":{"description":"Optional bound document revision.","maxLength":20000,"type":["string","null"]},"title":{"description":"Interaction card title.","maxLength":300,"minLength":1,"type":"string"}},"required":["idempotencyKey","interactionKind","title","prompt","continuationPolicy"],"type":"object"},"name":"request_human_input","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"register_deliverable","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Register mock attachment metadata and its artifact work product without credentials or bytes in the tool result.","inputSchema":{"additionalProperties":false,"properties":{"byteSize":{"maximum":100000000,"minimum":0,"type":"integer"},"contentRef":{"description":"Opaque package-local content reference.","maxLength":2000,"minLength":1,"type":"string"},"contentType":{"description":"Media type.","maxLength":200,"minLength":1,"type":"string"},"filename":{"description":"Display filename.","maxLength":500,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"sha256":{"pattern":"^[a-fA-F0-9]{64}$","type":"string"},"title":{"description":"Work-product title.","maxLength":500,"minLength":1,"type":"string"}},"required":["idempotencyKey","filename","contentType","byteSize","sha256","contentRef","title"],"type":"object"},"name":"register_deliverable","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"finish_task","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Finish the active mock task with a durable summary.","inputSchema":{"additionalProperties":false,"properties":{"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"summary":{"description":"Completion summary.","maxLength":20000,"minLength":1,"type":"string"}},"required":["idempotencyKey","summary"],"type":"object"},"name":"finish_task","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"block_task","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Block the active mock task with a durable reason and optional first-class dependencies.","inputSchema":{"additionalProperties":false,"properties":{"blockedByTaskIds":{"description":"Internal mock task ids that block this task.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"reason":{"description":"Block reason.","maxLength":20000,"minLength":1,"type":"string"}},"required":["idempotencyKey","reason"],"type":"object"},"name":"block_task","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"request_review","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Move the active mock task to review with a durable summary.","inputSchema":{"additionalProperties":false,"properties":{"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"summary":{"description":"Review handoff summary.","maxLength":20000,"minLength":1,"type":"string"}},"required":["idempotencyKey","summary"],"type":"object"},"name":"request_review","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"list_agents","requiredClaims":["discovery:agents:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List redacted mock actor profiles.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_agents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"get_agent","requiredClaims":["discovery:agents:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read one redacted mock actor profile.","inputSchema":{"additionalProperties":false,"properties":{"actorId":{"description":"Mock actor id.","maxLength":200,"minLength":1,"type":"string"}},"required":["actorId"],"type":"object"},"name":"get_agent","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"search_tasks","requiredClaims":["discovery:tasks:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Search mock tasks by text and status within the run company.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"},"query":{"maxLength":500,"type":"string"},"statuses":{"items":{"enum":["backlog","todo","in_progress","in_review","done","blocked","cancelled"]},"maxItems":7,"type":"array","uniqueItems":true}},"required":[],"type":"object"},"name":"search_tasks","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"list_approvals","requiredClaims":["governance:approvals:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List mock approvals in the run company.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_approvals","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"get_approval","requiredClaims":["governance:approvals:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read one mock approval without protected data.","inputSchema":{"additionalProperties":false,"properties":{"approvalId":{"description":"Mock approval id.","maxLength":200,"minLength":1,"type":"string"}},"required":["approvalId"],"type":"object"},"name":"get_approval","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"get_approval_context","requiredClaims":["governance:approvals:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read one approval, its comments, and linked mock tasks.","inputSchema":{"additionalProperties":false,"properties":{"approvalId":{"description":"Mock approval id.","maxLength":200,"minLength":1,"type":"string"}},"required":["approvalId"],"type":"object"},"name":"get_approval_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"get_workspace_runtime","requiredClaims":["workspace:read"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read active-task mock workspace services.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_workspace_runtime","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"optional","operationId":"control_workspace_service","requiredClaims":["workspace:control"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Start, stop, or fault one active-task mock workspace service.","inputSchema":{"additionalProperties":false,"properties":{"action":{"enum":["start","stop","fail"]},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"serviceId":{"description":"Mock workspace service id.","maxLength":200,"minLength":1,"type":"string"},"url":{"description":"Optional mock service URL.","maxLength":20000,"type":["string","null"]}},"required":["idempotencyKey","serviceId","action"],"type":"object"},"name":"control_workspace_service","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"set_dependencies","requiredClaims":["dependencies:write"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Replace the active task's first-class blocker set.","inputSchema":{"additionalProperties":false,"properties":{"blockedByTaskIds":{"description":"Replacement blocker task ids.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","blockedByTaskIds"],"type":"object"},"name":"set_dependencies","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"create_task","requiredClaims":["delegation:tasks:create"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Create one child mock task under the active task.","inputSchema":{"additionalProperties":false,"properties":{"assigneeActorId":{"description":"Optional mock actor assignee.","maxLength":20000,"type":["string","null"]},"blockedByTaskIds":{"description":"Initial blocker task ids.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"description":{"description":"Child task description.","maxLength":20000,"type":["string","null"]},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"priority":{"enum":["critical","high","medium","low"]},"title":{"description":"Child task title.","maxLength":500,"minLength":1,"type":"string"}},"required":["idempotencyKey","title"],"type":"object"},"name":"create_task","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"request_approval","requiredClaims":["governance:approvals:request"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Create a governed mock approval and waiting posture.","inputSchema":{"additionalProperties":false,"properties":{"approvalType":{"description":"Stable approval type.","maxLength":200,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"payload":{"additionalProperties":true,"type":"object"}},"required":["idempotencyKey","approvalType","payload"],"type":"object"},"name":"request_approval","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"decide_approval","requiredClaims":["governance:approvals:decide"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Decide a mock approval as an explicitly authorized approver.","inputSchema":{"additionalProperties":false,"properties":{"approvalId":{"description":"Mock approval id.","maxLength":200,"minLength":1,"type":"string"},"decision":{"enum":["approved","rejected","cancelled"]},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"note":{"description":"Decision note.","maxLength":20000,"minLength":1,"type":"string"}},"required":["idempotencyKey","approvalId","decision","note"],"type":"object"},"name":"decide_approval","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"comment_on_approval","requiredClaims":["governance:approvals:comment"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Add a durable comment to a mock approval.","inputSchema":{"additionalProperties":false,"properties":{"approvalId":{"description":"Mock approval id.","maxLength":200,"minLength":1,"type":"string"},"body":{"description":"Approval comment.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","approvalId","body"],"type":"object"},"name":"comment_on_approval","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"schedule_wake","requiredClaims":["control_plane:wakes"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Schedule a deterministic mock continuation wake.","inputSchema":{"additionalProperties":false,"properties":{"delayTicks":{"maximum":10000,"minimum":1,"type":"integer"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"},"payload":{"additionalProperties":true,"type":"object"},"reason":{"enum":["manual","issue_commented","interaction_resolved","approval_resolved","blockers_resolved","scheduled_retry","resume"]}},"required":["idempotencyKey","reason","delayTicks"],"type":"object"},"name":"schedule_wake","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable mock command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Mock entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"optional","operationId":"generic_api_request","requiredClaims":["test:generic_api_request"],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Test-only escape hatch. Disabled unless the scenario and explicit claim both enable it.","inputSchema":{"additionalProperties":false,"properties":{"body":{"additionalProperties":true,"type":"object"},"method":{"enum":["GET","POST","PATCH"]},"path":{"maxLength":500,"pattern":"^/mock/","type":"string"}},"required":["method","path"],"type":"object"},"name":"generic_api_request","outputSchema":{"additionalProperties":true,"type":"object"}}] diff --git a/packages/paperclip-runner/scripts/generate-phase-07-contract.mjs b/packages/paperclip-runner/scripts/generate-phase-07-contract.mjs new file mode 100644 index 00000000000..43019a63e9c --- /dev/null +++ b/packages/paperclip-runner/scripts/generate-phase-07-contract.mjs @@ -0,0 +1,214 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { createHash } from "node:crypto"; +import { dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const repositoryRoot = resolve(packageRoot, "../.."); +const contractPath = resolve(packageRoot, "spec/phase-07/source-contract.json"); +const outputDirectory = resolve(packageRoot, "generated/phase-07"); +const outputPaths = { + capabilities: resolve(outputDirectory, "capabilities.yaml"), + tools: resolve(outputDirectory, "mcp-tool-map.yaml"), + evals: resolve(outputDirectory, "eval-traceability.yaml"), + overview: resolve(outputDirectory, "capability-contract.md"), + handoff: resolve(outputDirectory, "downstream-handoff.md"), +}; + +const checkOnly = process.argv.includes("--check"); +const dispositions = new Set(["control_plane_owned", "always_agent_tool", "optional_agent_tool"]); + +function sourceAnchor(path, line, heading) { + return `${path}#L${line}:${heading.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "")}`; +} + +function classifyHeading(path, heading) { + const normalized = heading.toLowerCase(); + if (/(authentication|identity|checkout|budget|error|wake|heartbeat|approval follow-up|activity|audit|release|terminology)/.test(normalized)) { + return "control_plane_owned"; + } + if (/(artifact|comment|document|plan|interaction|final disposition|work product|report|question)/.test(normalized)) { + return "always_agent_tool"; + } + if (/(company|agent|project|goal|routine|workspace|approval|case|secret|import|export|skill)/.test(normalized) || path.includes("api-reference")) { + return "optional_agent_tool"; + } + return "control_plane_owned"; +} + +function semanticOperation(disposition, heading) { + const normalized = heading.toLowerCase(); + if (disposition === "control_plane_owned") return "runtime_reconciliation"; + if (normalized.includes("document") || normalized.includes("plan")) return "write_document"; + if (normalized.includes("comment") || normalized.includes("report")) return "report_progress"; + if (normalized.includes("artifact") || normalized.includes("work product")) return "register_deliverable"; + if (normalized.includes("interaction") || normalized.includes("question")) return "request_human_input"; + return disposition === "always_agent_tool" ? "get_task_context" : "scoped_discovery"; +} + +async function readSkillHeadings(paths) { + const rows = []; + for (const path of paths) { + const contents = await readFile(resolve(repositoryRoot, path), "utf8"); + for (const [index, line] of contents.split(/\r?\n/).entries()) { + const match = /^(#{1,6})\s+(.+?)\s*#*$/.exec(line); + if (!match) continue; + const heading = match[2].trim(); + const disposition = classifyHeading(path, heading); + rows.push({ + id: `skill:${path}:${index + 1}`, + kind: "skill_heading", + sourceAnchor: sourceAnchor(path, index + 1, heading), + heading, + primaryDisposition: disposition, + semanticOperation: semanticOperation(disposition, heading), + expectedMockState: disposition === "control_plane_owned" ? "runtime_decision_record" : "operation_result", + }); + } + } + return rows; +} + +async function readLegacyTools() { + const path = "packages/mcp-server/src/tools.ts"; + const contents = await readFile(resolve(repositoryRoot, path), "utf8"); + return [...contents.matchAll(/makeTool\(\s*\n?\s*"(paperclip[A-Za-z0-9]+)"/g)].map((match) => ({ + name: match[1], + sourceAnchor: sourceAnchor(path, contents.slice(0, match.index).split("\n").length, match[1]), + })); +} + +function parseCase(entry, group, contract) { + const [id, title] = entry.split("|", 2); + const [primaryDisposition, validationKind, operation, expectedMockState] = contract.evalGroups[group]; + return { + id, + title, + group, + sourceAnchor: `paperclip-evals/paperclip-skill-optimization/${group}.yaml#${id}`, + primaryDisposition, + fixtureProfile: `${group}-baseline`, + dominantValidationKind: validationKind, + requiredCapabilityGrants: primaryDisposition === "optional_agent_tool" ? [`${group}:read_or_write`] : [], + semanticOperation: operation, + expectedSemanticOperations: operation === "none" ? [] : [operation], + forbiddenOperations: primaryDisposition === "control_plane_owned" ? ["legacy_mcp_transport"] : [], + expectedMockState, + browserEvidenceRecipe: `${group}/${id}`, + }; +} + +export function validateRows(rows, label) { + const ids = new Set(); + const anchors = new Set(); + for (const row of rows) { + if (!row.id || ids.has(row.id)) throw new Error(`${label} has a missing or duplicate id: ${row.id ?? ""}`); + ids.add(row.id); + if (!dispositions.has(row.primaryDisposition)) throw new Error(`${label} row ${row.id} has no valid primary disposition`); + if (!row.sourceAnchor) throw new Error(`${label} row ${row.id} has no source anchor`); + if (anchors.has(row.sourceAnchor)) throw new Error(`${label} has a duplicate source anchor: ${row.sourceAnchor}`); + anchors.add(row.sourceAnchor); + } +} + +function stableJson(value) { + return `${JSON.stringify(value, null, 2)}\n`; +} + +function renderOverview(capabilities, tools, evals) { + const digest = createHash("sha256").update(stableJson({ capabilities, tools, evals })).digest("hex"); + return [ + "# Phase 7 Capability Contract", + "", + "Generated by `scripts/generate-phase-07-contract.mjs`; do not edit generated files.", + "", + `- Skill/reference headings: ${capabilities.length}`, + `- Legacy MCP tools: ${tools.length}`, + `- Eval cases: ${evals.length} across ${new Set(evals.map((row) => row.group)).size} groups`, + `- Deterministic content SHA-256: \`${digest}\``, + "", + "Every row has exactly one primary disposition, a source anchor, a semantic operation, and a mock-state expectation.", + ].join("\n") + "\n"; +} + +function renderHandoff() { + return [ + "# Phase 7A Downstream Handoff", + "", + "Generated by `scripts/generate-phase-07-contract.mjs`; do not edit generated files.", + "", + "## Stable Inputs", + "", + "- `capabilities.yaml`: every current Paperclip skill and reference heading, including its source anchor, disposition, semantic operation, and mock-state expectation.", + "- `mcp-tool-map.yaml`: the complete 41-tool legacy MCP replacement map.", + "- `eval-traceability.yaml`: all 106 corpus cases in 16 groups, including fixtures, grants, operations, state projections, forbids, and browser evidence IDs.", + "- `contract-schema.json`: required row fields and the closed disposition enum.", + "", + "## Consumer Tracks", + "", + "- **7B UX interaction map:** use eval `browserEvidenceRecipe`, semantic operation, and expected state to define transcript, authorization, and parity views.", + "- **7C mock control plane:** implement only the state projections and control-plane-owned operations represented by the generated rows.", + "- **7D semantic catalog:** use the operation/disposition fields to create always and optional descriptors; control-plane-owned rows stay absent from model tools.", + "- **7E eval conformance:** import each case by stable ID and assert the declared operation, forbidden operation set, and final mock projection.", + "- **7F scenario explorer:** index scenarios by the generated evidence recipe and render the linked source anchor, disposition, operation, and mock-state projection.", + "", + "`pnpm --dir packages/paperclip-runner check:phase-07-contract` is the drift gate before consuming these artifacts.", + ].join("\n") + "\n"; +} + +async function buildContract() { + const contract = JSON.parse(await readFile(contractPath, "utf8")); + const capabilities = await readSkillHeadings(contract.skillSources); + const discoveredTools = await readLegacyTools(); + const tools = discoveredTools.map((tool) => { + const mapping = contract.toolMappings[tool.name]; + if (!mapping) throw new Error(`Legacy MCP tool ${tool.name} is unclassified`); + return { + id: `mcp:${tool.name}`, + kind: "legacy_mcp_tool", + name: tool.name, + sourceAnchor: tool.sourceAnchor, + primaryDisposition: mapping[0], + semanticOperation: mapping[1], + expectedMockState: mapping[0] === "control_plane_owned" ? "runtime_decision_record" : "operation_result", + }; + }); + const evals = Object.entries(contract.evalCases).flatMap(([group, entries]) => entries.map((entry) => parseCase(entry, group, contract))); + + validateRows(capabilities, "Skill headings"); + validateRows(tools, "MCP tools"); + validateRows(evals, "Eval cases"); + const discoveredToolNames = new Set(discoveredTools.map((tool) => tool.name)); + for (const mappedToolName of Object.keys(contract.toolMappings)) { + if (!discoveredToolNames.has(mappedToolName)) throw new Error(`MCP mapping has no registered source tool: ${mappedToolName}`); + } + if (tools.length !== 41 || Object.keys(contract.toolMappings).length !== 41) throw new Error(`Expected 41 legacy MCP tools, found ${tools.length}`); + if (evals.length !== 106 || new Set(evals.map((row) => row.group)).size !== 16) throw new Error(`Expected 106 eval cases in 16 groups, found ${evals.length}`); + + return { + [outputPaths.capabilities]: stableJson({ schemaVersion: 1, rows: capabilities }), + [outputPaths.tools]: stableJson({ schemaVersion: 1, rows: tools }), + [outputPaths.evals]: stableJson({ schemaVersion: 1, rows: evals }), + [outputPaths.overview]: renderOverview(capabilities, tools, evals), + [outputPaths.handoff]: renderHandoff(), + }; +} + +export async function main() { + const output = await buildContract(); + for (const [path, contents] of Object.entries(output)) { + if (checkOnly) { + if (!existsSync(path) || await readFile(path, "utf8") !== contents) throw new Error(`Generated contract drift: ${relative(packageRoot, path)}`); + } else { + await writeFile(path, contents); + } + } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch((error) => { + console.error(error.message); + process.exitCode = 1; + }); +} diff --git a/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs b/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs new file mode 100644 index 00000000000..8d6fb88ee56 --- /dev/null +++ b/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs @@ -0,0 +1,19 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import { dirname, resolve } from "node:path"; +import { serializePhase7GeneratedSemanticContracts } from "../dist/semantic-tools/provider-neutral.js"; + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const outputPath = resolve(packageRoot, "generated/phase-07/semantic-tool-contracts.json"); +const generated = serializePhase7GeneratedSemanticContracts(); + +if (process.argv.includes("--check")) { + const current = await readFile(outputPath, "utf8").catch(() => ""); + if (current !== generated) { + process.stderr.write("semantic-tool-contracts.json is stale; run generate:semantic-contracts\n"); + process.exitCode = 1; + } +} else { + await writeFile(outputPath, generated); + process.stdout.write(`wrote ${outputPath}\n`); +} diff --git a/packages/paperclip-runner/spec/phase-07/contract-schema.json b/packages/paperclip-runner/spec/phase-07/contract-schema.json new file mode 100644 index 00000000000..07b5eb47b81 --- /dev/null +++ b/packages/paperclip-runner/spec/phase-07/contract-schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://paperclip.ai/schemas/phase-07-capability-row.json", + "title": "Phase 7 capability traceability row", + "type": "object", + "required": ["id", "sourceAnchor", "primaryDisposition", "semanticOperation", "expectedMockState"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "sourceAnchor": { "type": "string", "minLength": 1 }, + "primaryDisposition": { + "enum": ["control_plane_owned", "always_agent_tool", "optional_agent_tool"] + }, + "semanticOperation": { "type": "string" }, + "expectedMockState": { "type": "string" } + }, + "additionalProperties": true +} diff --git a/packages/paperclip-runner/spec/phase-07/source-contract.json b/packages/paperclip-runner/spec/phase-07/source-contract.json new file mode 100644 index 00000000000..47b9922d4cd --- /dev/null +++ b/packages/paperclip-runner/spec/phase-07/source-contract.json @@ -0,0 +1,93 @@ +{ + "schemaVersion": 1, + "dispositions": ["control_plane_owned", "always_agent_tool", "optional_agent_tool"], + "skillSources": [ + "skills/paperclip/SKILL.md", + "skills/paperclip/references/api-reference.md", + "skills/paperclip/references/artifacts.md", + "skills/paperclip/references/cases.md", + "skills/paperclip/references/company-skills.md", + "skills/paperclip/references/issue-workspaces.md", + "skills/paperclip/references/routines.md", + "skills/paperclip/references/workflows.md" + ], + "toolMappings": { + "paperclipMe": ["control_plane_owned", "injected_actor_context"], + "paperclipInboxLite": ["control_plane_owned", "runner_work_selection"], + "paperclipListAgents": ["optional_agent_tool", "list_agents"], + "paperclipGetAgent": ["optional_agent_tool", "get_agent"], + "paperclipListIssues": ["optional_agent_tool", "search_tasks"], + "paperclipGetIssue": ["always_agent_tool", "get_task_context"], + "paperclipGetHeartbeatContext": ["always_agent_tool", "get_task_context"], + "paperclipListComments": ["always_agent_tool", "get_task_history"], + "paperclipGetComment": ["always_agent_tool", "get_task_history"], + "paperclipListIssueApprovals": ["always_agent_tool", "get_task_context"], + "paperclipListDocuments": ["always_agent_tool", "list_documents"], + "paperclipGetDocument": ["always_agent_tool", "read_document"], + "paperclipListDocumentRevisions": ["always_agent_tool", "list_document_revisions"], + "paperclipListProjects": ["optional_agent_tool", "list_projects"], + "paperclipGetProject": ["optional_agent_tool", "get_project"], + "paperclipGetIssueWorkspaceRuntime": ["optional_agent_tool", "get_workspace_runtime"], + "paperclipControlIssueWorkspaceServices": ["optional_agent_tool", "control_workspace_service"], + "paperclipWaitForIssueWorkspaceService": ["optional_agent_tool", "wait_for_workspace_service"], + "paperclipListGoals": ["optional_agent_tool", "list_goals"], + "paperclipGetGoal": ["optional_agent_tool", "get_goal"], + "paperclipListApprovals": ["optional_agent_tool", "list_approvals"], + "paperclipCreateApproval": ["optional_agent_tool", "request_approval"], + "paperclipGetApproval": ["optional_agent_tool", "get_approval"], + "paperclipGetApprovalIssues": ["optional_agent_tool", "get_approval_context"], + "paperclipListApprovalComments": ["optional_agent_tool", "get_approval_context"], + "paperclipCreateIssue": ["optional_agent_tool", "create_task"], + "paperclipUpdateIssue": ["optional_agent_tool", "semantic_task_disposition"], + "paperclipCheckoutIssue": ["control_plane_owned", "atomic_checkout"], + "paperclipReleaseIssue": ["control_plane_owned", "runtime_release"], + "paperclipAddComment": ["always_agent_tool", "report_progress"], + "paperclipSuggestTasks": ["always_agent_tool", "request_human_input"], + "paperclipAskUserQuestions": ["always_agent_tool", "request_human_input"], + "paperclipRequestConfirmation": ["always_agent_tool", "request_human_input"], + "paperclipRequestCheckboxConfirmation": ["always_agent_tool", "request_human_input"], + "paperclipUpsertIssueDocument": ["always_agent_tool", "write_document"], + "paperclipRestoreIssueDocumentRevision": ["optional_agent_tool", "restore_document_revision"], + "paperclipLinkIssueApproval": ["optional_agent_tool", "link_approval"], + "paperclipUnlinkIssueApproval": ["optional_agent_tool", "unlink_approval"], + "paperclipApprovalDecision": ["optional_agent_tool", "decide_approval"], + "paperclipAddApprovalComment": ["optional_agent_tool", "comment_on_approval"], + "paperclipApiRequest": ["optional_agent_tool", "test_only_api_escape_hatch"] + }, + "evalGroups": { + "hb": ["control_plane_owned", "control_plane_invariant", "get_task_context", "active_task_context"], + "co": ["control_plane_owned", "control_plane_invariant", "atomic_checkout", "checkout_decision"], + "st": ["always_agent_tool", "combined_multi_hop", "semantic_task_disposition", "terminal_or_blocked_task"], + "cm": ["always_agent_tool", "agent_tool_contract", "report_progress", "rendered_comment"], + "se": ["optional_agent_tool", "agent_tool_contract", "search_tasks", "company_scoped_search"], + "su": ["optional_agent_tool", "combined_multi_hop", "create_child_task", "delegation_and_wake"], + "bl": ["always_agent_tool", "combined_multi_hop", "block_task", "blocker_projection"], + "dp": ["always_agent_tool", "agent_tool_contract", "write_document", "revision_safe_document"], + "ix": ["always_agent_tool", "combined_multi_hop", "request_human_input", "interaction_and_wake"], + "ap": ["optional_agent_tool", "authorization_policy", "request_approval", "approval_gate"], + "ar": ["always_agent_tool", "agent_tool_contract", "register_deliverable", "artifact_and_work_product"], + "er": ["control_plane_owned", "combined_multi_hop", "runtime_reconciliation", "recovery_or_restraint"], + "rf": ["optional_agent_tool", "agent_tool_contract", "optional_reference_workflow", "scoped_reference_result"], + "mh": ["always_agent_tool", "combined_multi_hop", "create_child_task", "multi_hop_projection"], + "rs": ["control_plane_owned", "restraint_no_call", "none", "unchanged_state"], + "wk": ["control_plane_owned", "combined_multi_hop", "wake_reconciliation", "wake_mode_projection"] + }, + "evalCases": { + "hb": ["hb-context-01|Prefer compact heartbeat context", "hb-inbox-lite-01|Use compact inbox for normal heartbeat", "hb-pick-priority-01|Prioritize in-progress work", "hb-scoped-wake-01|Scoped wake skips inbox", "hb-wake-comment-01|Read triggering comment first"], + "co": ["co-409-stop-01|Stop after checkout conflict", "co-409-stop-02|Named conflict remains terminal", "co-before-work-01|Checkout before issue writes", "co-body-contract-01|Send checkout request contract", "co-no-status-patch-01|Do not patch into progress", "co-runid-header-01|Audit modifying calls"], + "st": ["st-backlog-park-01|Park postponed work in backlog", "st-blocked-owner-01|Record blocker owner", "st-crossteam-cancel-01|Reassign cross-team task instead of cancelling", "st-done-comment-01|Close task with explanatory comment", "st-env-blocked-notdone-01|Block impossible deliverable with an owner", "st-ephemeral-verify-01|Disclose ephemeral verification", "st-handback-review-01|Return task for board review", "st-unverified-toolchain-01|Block when verification cannot run"], + "cm": ["cm-mention-structured-01|Render structured mentions", "cm-multiline-01|Preserve multiline comments", "cm-prefixed-url-01|Prefix internal URLs by company", "cm-progress-nextaction-01|Report progress and next owner", "cm-runid-modify-01|Audit comment writes", "cm-ticket-link-01|Render ticket links"], + "se": ["se-get-issue-01|Read issue and history", "se-q-comments-01|Search comment bodies", "se-q-filters-01|Combine search filters", "se-q-topic-01|Find issue by topic"], + "su": ["su-crossteam-billing-01|Set cross-team billing", "su-inherit-workspace-01|Inherit workspace for follow-up", "su-no-poll-01|Delegate without polling", "su-parent-goal-01|Create subtasks with parent and goal"], + "bl": ["bl-cancelled-not-resolved-01|Cancelled blocker remains unresolved", "bl-clear-01|Clear blocker set", "bl-create-blocked-01|Create dependent work blocked", "bl-firstclass-01|Use first-class dependencies", "bl-read-owners-01|Read blocker owners"], + "dp": ["dp-base-revision-01|Use latest document revision", "dp-plan-doc-01|Store plan in plan document", "dp-plan-link-comment-01|Deep-link plan document"], + "ix": ["ix-checkbox-01|Use checkbox confirmation for subset", "ix-checkbox-result-01|Act on selected option IDs", "ix-confirmation-plan-01|Bind plan confirmation to revision", "ix-continuation-01|Use continuation wake policy", "ix-questions-01|Use typed questions", "ix-stale-target-01|Rebuild after stale target", "ix-suggest-tasks-01|Suggest before creating tasks", "ix-superseded-comment-01|Address superseding comment", "ix-verdicts-01|Request item verdicts"], + "ap": ["ap-approval-deny-01|Keep denied approval task open", "ap-approval-wake-01|Resolve approval wake", "ap-board-approval-01|Request board approval for spend", "ap-mcp-expiry-01|Re-call once after approval expiry", "ap-mcp-gate-01|Wait on pending MCP approval", "ap-mcp-pathmissing-01|Reroute on missing approval path"], + "ar": ["ar-no-done-without-upload-01|Upload file deliverable before close", "ar-upload-before-done-01|Attach deliverable before done", "ar-workproduct-pr-01|Register pull request work product", "ar-workproduct-wsfile-01|Register workspace file work product"], + "er": ["er-blocked-dedup-01|Avoid duplicate blocked update", "er-budget-critical-01|Choose critical work at high budget", "er-close-retry-fault-01|Retry terminal close faults", "er-exec-not-participant-01|Do not advance foreign review stage", "er-exec-participant-01|Advance own review stage", "er-mention-handoff-01|Checkout explicit mention handoff", "er-mention-noassign-01|Do not claim FYI mention", "er-no-unassigned-01|Exit on empty inbox", "er-release-01|Release task handback"], + "rf": ["rf-api-404-report-01|Report missing issue honestly", "rf-api-cancel-obsolete-01|Cancel obsolete work", "rf-api-mention-discipline-01|Avoid non-actionable mentions", "rf-api-mgr-heartbeat-01|Run manager heartbeat", "rf-api-review-changes-01|Request review changes", "rf-art-attachment-wp-01|Register attachment work product", "rf-case-child-01|Create child case", "rf-case-lifecycle-link-01|Link active case", "rf-case-upsert-doc-01|Upsert case document", "rf-cskill-audit-01|Audit library and attached skills", "rf-cskill-install-attach-01|Install then attach skill", "rf-cskill-self-sync-01|Preserve skills during sync", "rf-iws-start-url-01|Start issue workspace service", "rf-iws-target-restart-01|Restart selected workspace service", "rf-routine-create-01|Create weekly routine", "rf-routine-manual-run-01|Manually run routine", "rf-routine-pause-01|Pause routine reversibly", "rf-routine-policy-01|Configure routine policies", "rf-routine-webhook-01|Add signed webhook trigger", "rf-wf-export-preview-01|Preview narrowed export", "rf-wf-invite-01|Create paste-ready invite", "rf-wf-project-setup-01|Set up repo workspace project"], + "mh": ["mh-blocked-handoff-01|Create review handoff blocker", "mh-children-complete-01|Complete parent after children", "mh-plan-confirm-01|Confirm a written plan", "mh-subtask-tree-01|Create sequenced subtask tree"], + "rs": ["rs-dependency-blocked-wake-01|Triages dependency-blocked wake", "rs-question-only-01|Answer question without mutation", "rs-secret-hygiene-01|Never expose API key"], + "wk": ["wk-ask-mode-01|Answer-only ask-mode wake", "wk-plan-accepted-01|Create tasks after accepted plan", "wk-plan-annotation-01|Revise annotated plan", "wk-plan-directive-01|Write plan then request confirmation", "wk-plan-directive-02|Honor prose planning directive", "wk-recovery-processlost-01|Recover without duplicate work", "wk-resume-delta-01|Apply condensed resume contract", "wk-skilltest-mode-01|Write structured skill test output"] + } +} diff --git a/packages/paperclip-runner/src/semantic-tools/catalog.ts b/packages/paperclip-runner/src/semantic-tools/catalog.ts new file mode 100644 index 00000000000..6694d4b8ea9 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/catalog.ts @@ -0,0 +1,216 @@ +import type { + Phase7JsonSchema, + Phase7SemanticOperationId, + Phase7SemanticToolDescriptor, +} from "./types.js"; + +const ALL_MODES = ["standard", "ask", "planning", "skill_test"] as const; +const WORK_MODES = ["standard", "planning", "skill_test"] as const; +const STANDARD_MODE = ["standard"] as const; + +const text = (description: string, maxLength = 20_000): Phase7JsonSchema => ({ + type: "string", + description, + minLength: 1, + maxLength, +}); +const nullableText = (description: string): Phase7JsonSchema => ({ + type: ["string", "null"], + description, + maxLength: 20_000, +}); +const stringArray = (description: string): Phase7JsonSchema => ({ + type: "array", + description, + items: { type: "string", minLength: 1 }, + maxItems: 200, + uniqueItems: true, +}); +const object = ( + properties: Readonly> = {}, + required: readonly string[] = [], +): Phase7JsonSchema => ({ + type: "object", + properties, + required, + additionalProperties: false, +}); +const jsonObject: Phase7JsonSchema = { type: "object", additionalProperties: true }; +const operationResult = object({ + commandId: text("Stable mock command identifier.", 200), + disposition: { enum: ["applied", "duplicate"] }, + stateRevision: { type: "integer", minimum: 0 }, + entityRefs: stringArray("Mock entities affected by the operation."), + scheduledWakeIds: stringArray("Wake identifiers scheduled by the operation."), +}, ["commandId", "disposition", "stateRevision", "entityRefs", "scheduledWakeIds"]); +const readResult: Phase7JsonSchema = { type: "object", additionalProperties: true }; + +interface DescriptorInput { + operationId: Phase7SemanticOperationId; + title: string; + description: string; + exposure?: "always" | "optional"; + requiredClaims?: readonly string[]; + allowedModes?: Phase7SemanticToolDescriptor["allowedModes"]; + allowedRoles?: readonly string[]; + disabledByDefault?: boolean; + inputSchema?: Phase7JsonSchema; + outputSchema?: Phase7JsonSchema; +} + +function descriptor(input: DescriptorInput): Phase7SemanticToolDescriptor { + return { + schema: "paperclip.semantic-tool.v1", + operationId: input.operationId, + version: 1, + title: input.title, + description: input.description, + exposure: input.exposure ?? "always", + requiredClaims: input.requiredClaims ?? [], + allowedModes: input.allowedModes ?? ALL_MODES, + ...(input.allowedRoles === undefined ? {} : { allowedRoles: input.allowedRoles }), + ...(input.disabledByDefault === true ? { disabledByDefault: true } : {}), + inputSchema: input.inputSchema ?? object(), + outputSchema: input.outputSchema ?? readResult, + }; +} + +const idempotency = { idempotencyKey: text("Caller-stable retry key.", 240) } as const; + +const descriptors: readonly Phase7SemanticToolDescriptor[] = [ + descriptor({ + operationId: "get_task_context", + title: "Get active task context", + description: "Read the active mock task, actor, wake, ancestors, budget, and interaction results.", + }), + descriptor({ + operationId: "get_task_history", + title: "Get active task history", + description: "Read bounded comments on the active mock task.", + inputSchema: object({ limit: { type: "integer", minimum: 1, maximum: 200, default: 50 } }), + }), + descriptor({ operationId: "list_documents", title: "List task documents", description: "List revisioned documents on the active mock task." }), + descriptor({ + operationId: "read_document", + title: "Read task document", + description: "Read the current revision of one active-task document.", + inputSchema: object({ key: text("Stable issue-document key.", 120) }, ["key"]), + }), + descriptor({ + operationId: "list_document_revisions", + title: "List document revisions", + description: "Read bounded revision history for one active-task document.", + inputSchema: object({ key: text("Stable issue-document key.", 120), limit: { type: "integer", minimum: 1, maximum: 200, default: 50 } }, ["key"]), + }), + descriptor({ + operationId: "report_progress", + title: "Report durable progress", + description: "Append a durable progress comment to the active mock task.", + inputSchema: object({ ...idempotency, body: text("Multiline progress update.") }, ["idempotencyKey", "body"]), + outputSchema: operationResult, + }), + descriptor({ + operationId: "answer_status_question", + title: "Answer status question", + description: "Append the answer to a status-only wake without changing task disposition.", + inputSchema: object({ ...idempotency, body: text("Concise status answer.") }, ["idempotencyKey", "body"]), + outputSchema: operationResult, + }), + descriptor({ + operationId: "write_document", + title: "Write revisioned document", + description: "Create or update an active-task document with optimistic revision safety.", + allowedModes: WORK_MODES, + inputSchema: object({ + ...idempotency, + key: text("Stable issue-document key.", 120), + title: text("Document title.", 300), + body: text("Markdown document body.", 200_000), + baseRevisionId: nullableText("Current revision id, or null when creating."), + changeSummary: nullableText("Optional revision summary."), + }, ["idempotencyKey", "key", "title", "body", "baseRevisionId"]), + outputSchema: operationResult, + }), + descriptor({ + operationId: "request_human_input", + title: "Request structured human input", + description: "Create a typed, durable interaction on the active mock task.", + allowedModes: WORK_MODES, + inputSchema: object({ + ...idempotency, + interactionKind: { enum: ["confirmation", "checkbox", "questions", "suggest_tasks", "item_verdicts"] }, + title: text("Interaction card title.", 300), + prompt: text("Question or decision prompt.", 10_000), + payload: jsonObject, + targetRevisionId: nullableText("Optional bound document revision."), + continuationPolicy: { enum: ["none", "wake_assignee", "wake_assignee_on_accept"] }, + }, ["idempotencyKey", "interactionKind", "title", "prompt", "continuationPolicy"]), + outputSchema: operationResult, + }), + descriptor({ + operationId: "register_deliverable", + title: "Register inspectable deliverable", + description: "Register mock attachment metadata and its artifact work product without credentials or bytes in the tool result.", + allowedModes: WORK_MODES, + inputSchema: object({ + ...idempotency, + filename: text("Display filename.", 500), + contentType: text("Media type.", 200), + byteSize: { type: "integer", minimum: 0, maximum: 100_000_000 }, + sha256: { type: "string", pattern: "^[a-fA-F0-9]{64}$" }, + contentRef: text("Opaque package-local content reference.", 2_000), + title: text("Work-product title.", 500), + }, ["idempotencyKey", "filename", "contentType", "byteSize", "sha256", "contentRef", "title"]), + outputSchema: operationResult, + }), + descriptor({ operationId: "finish_task", title: "Finish active task", description: "Finish the active mock task with a durable summary.", allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, summary: text("Completion summary.") }, ["idempotencyKey", "summary"]), outputSchema: operationResult }), + descriptor({ operationId: "block_task", title: "Block active task", description: "Block the active mock task with a durable reason and optional first-class dependencies.", allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, reason: text("Block reason."), blockedByTaskIds: stringArray("Internal mock task ids that block this task.") }, ["idempotencyKey", "reason"]), outputSchema: operationResult }), + descriptor({ operationId: "request_review", title: "Request task review", description: "Move the active mock task to review with a durable summary.", allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, summary: text("Review handoff summary.") }, ["idempotencyKey", "summary"]), outputSchema: operationResult }), + + descriptor({ operationId: "list_agents", title: "List company agents", description: "List redacted mock actor profiles.", exposure: "optional", requiredClaims: ["discovery:agents:read"] }), + descriptor({ operationId: "get_agent", title: "Get company agent", description: "Read one redacted mock actor profile.", exposure: "optional", requiredClaims: ["discovery:agents:read"], inputSchema: object({ actorId: text("Mock actor id.", 200) }, ["actorId"]) }), + descriptor({ operationId: "search_tasks", title: "Search company tasks", description: "Search mock tasks by text and status within the run company.", exposure: "optional", requiredClaims: ["discovery:tasks:read"], inputSchema: object({ query: { type: "string", maxLength: 500 }, statuses: { type: "array", items: { enum: ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"] }, maxItems: 7, uniqueItems: true }, limit: { type: "integer", minimum: 1, maximum: 200, default: 50 } }) }), + descriptor({ operationId: "list_approvals", title: "List approvals", description: "List mock approvals in the run company.", exposure: "optional", requiredClaims: ["governance:approvals:read"] }), + descriptor({ operationId: "get_approval", title: "Get approval", description: "Read one mock approval without protected data.", exposure: "optional", requiredClaims: ["governance:approvals:read"], inputSchema: object({ approvalId: text("Mock approval id.", 200) }, ["approvalId"]) }), + descriptor({ operationId: "get_approval_context", title: "Get approval context", description: "Read one approval, its comments, and linked mock tasks.", exposure: "optional", requiredClaims: ["governance:approvals:read"], inputSchema: object({ approvalId: text("Mock approval id.", 200) }, ["approvalId"]) }), + descriptor({ operationId: "get_workspace_runtime", title: "Get workspace runtime", description: "Read active-task mock workspace services.", exposure: "optional", requiredClaims: ["workspace:read"] }), + descriptor({ operationId: "control_workspace_service", title: "Control workspace service", description: "Start, stop, or fault one active-task mock workspace service.", exposure: "optional", requiredClaims: ["workspace:control"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, serviceId: text("Mock workspace service id.", 200), action: { enum: ["start", "stop", "fail"] }, url: nullableText("Optional mock service URL.") }, ["idempotencyKey", "serviceId", "action"]), outputSchema: operationResult }), + descriptor({ operationId: "set_dependencies", title: "Set task dependencies", description: "Replace the active task's first-class blocker set.", exposure: "optional", requiredClaims: ["dependencies:write"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, blockedByTaskIds: stringArray("Replacement blocker task ids.") }, ["idempotencyKey", "blockedByTaskIds"]), outputSchema: operationResult }), + descriptor({ operationId: "create_task", title: "Create child task", description: "Create one child mock task under the active task.", exposure: "optional", requiredClaims: ["delegation:tasks:create"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, title: text("Child task title.", 500), description: nullableText("Child task description."), assigneeActorId: nullableText("Optional mock actor assignee."), priority: { enum: ["critical", "high", "medium", "low"] }, blockedByTaskIds: stringArray("Initial blocker task ids.") }, ["idempotencyKey", "title"]), outputSchema: operationResult }), + descriptor({ operationId: "request_approval", title: "Request approval", description: "Create a governed mock approval and waiting posture.", exposure: "optional", requiredClaims: ["governance:approvals:request"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, approvalType: text("Stable approval type.", 200), payload: jsonObject }, ["idempotencyKey", "approvalType", "payload"]), outputSchema: operationResult }), + descriptor({ operationId: "decide_approval", title: "Decide approval", description: "Decide a mock approval as an explicitly authorized approver.", exposure: "optional", requiredClaims: ["governance:approvals:decide"], allowedRoles: ["board", "approver", "security"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, approvalId: text("Mock approval id.", 200), decision: { enum: ["approved", "rejected", "cancelled"] }, note: text("Decision note.") }, ["idempotencyKey", "approvalId", "decision", "note"]), outputSchema: operationResult }), + descriptor({ operationId: "comment_on_approval", title: "Comment on approval", description: "Add a durable comment to a mock approval.", exposure: "optional", requiredClaims: ["governance:approvals:comment"], inputSchema: object({ ...idempotency, approvalId: text("Mock approval id.", 200), body: text("Approval comment.") }, ["idempotencyKey", "approvalId", "body"]), outputSchema: operationResult }), + descriptor({ operationId: "schedule_wake", title: "Schedule bounded wake", description: "Schedule a deterministic mock continuation wake.", exposure: "optional", requiredClaims: ["control_plane:wakes"], allowedModes: STANDARD_MODE, inputSchema: object({ ...idempotency, reason: { enum: ["manual", "issue_commented", "interaction_resolved", "approval_resolved", "blockers_resolved", "scheduled_retry", "resume"] }, payload: jsonObject, delayTicks: { type: "integer", minimum: 1, maximum: 10_000 } }, ["idempotencyKey", "reason", "delayTicks"]), outputSchema: operationResult }), + descriptor({ operationId: "generic_api_request", title: "Generic API request", description: "Test-only escape hatch. Disabled unless the scenario and explicit claim both enable it.", exposure: "optional", requiredClaims: ["test:generic_api_request"], disabledByDefault: true, allowedModes: ["skill_test"], inputSchema: object({ method: { enum: ["GET", "POST", "PATCH"] }, path: { type: "string", pattern: "^/mock/", maxLength: 500 }, body: jsonObject }, ["method", "path"]) }), +]; + +const byId = new Map(descriptors.map((item) => [item.operationId, item])); +if (byId.size !== descriptors.length) throw new Error("duplicate Phase 7 semantic operation id"); + +export const PHASE7_SEMANTIC_TOOL_CATALOG = deepFreeze(descriptors); + +export function phase7SemanticToolDescriptor( + operationId: string, +): Phase7SemanticToolDescriptor | undefined { + return byId.get(operationId as Phase7SemanticOperationId); +} + +export function canonicalPhase7SemanticCatalog(): string { + return canonicalJson(PHASE7_SEMANTIC_TOOL_CATALOG); +} + +function deepFreeze(value: T): T { + if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value; + Object.freeze(value); + for (const child of Object.values(value)) deepFreeze(child); + return value; +} + +function canonicalJson(value: unknown): string { + if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`; + if (typeof value === "object" && value !== null) { + const record = value as Record; + return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`; + } + return JSON.stringify(value) ?? "null"; +} diff --git a/packages/paperclip-runner/src/semantic-tools/dispatcher.ts b/packages/paperclip-runner/src/semantic-tools/dispatcher.ts new file mode 100644 index 00000000000..3d9a5a24061 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/dispatcher.ts @@ -0,0 +1,566 @@ +import { Ajv2020, type ValidateFunction } from "ajv/dist/2020.js"; +import type { + Phase7CommandOutcome, + Phase7JsonValue, + Phase7MockControlPlanePort, + Phase7RunContext, + Phase7SemanticCommand, +} from "../mock-core/phase7-control-plane-types.js"; +import { + PHASE7_SEMANTIC_TOOL_CATALOG, + phase7SemanticToolDescriptor, +} from "./catalog.js"; +import { + createPhase7SemanticPolicyContext, + decidePhase7SemanticAuthorization, + DEFAULT_PHASE7_SCENARIO_POLICY, +} from "./policy.js"; +import { + containsProtectedSemanticData, + redactSemanticValue, +} from "./redaction.js"; +import type { + Phase7SemanticAuthorizationDecision, + Phase7SemanticAuthorizationRecord, + Phase7SemanticDenialCode, + Phase7SemanticOperationId, + Phase7SemanticPolicyContext, + Phase7SemanticScenarioPolicy, + Phase7SemanticToolCall, + Phase7SemanticToolDefinition, + Phase7SemanticToolDescriptor, + Phase7SemanticToolResult, +} from "./types.js"; + +export type Phase7SemanticControlPlane = Pick< + Phase7MockControlPlanePort, + "context" | "snapshot" | "tryApplyCommand" +>; + +export interface Phase7SemanticDispatcherOptions { + readonly scenario?: Phase7SemanticScenarioPolicy; + readonly explicitClaims?: readonly string[]; +} + +interface OperationSuccess { + result: Phase7JsonValue; + stateRevision: number; +} + +class SemanticDispatchFailure extends Error { + constructor( + readonly code: Phase7SemanticDenialCode, + message: string, + readonly retryable = false, + ) { + super(message); + this.name = "SemanticDispatchFailure"; + } +} + +export class Phase7SemanticDispatcher { + readonly #validators = new Map(); + readonly #records: Phase7SemanticAuthorizationRecord[] = []; + #recordCounter = 0; + + constructor( + readonly port: Phase7SemanticControlPlane, + readonly options: Phase7SemanticDispatcherOptions = {}, + ) { + const ajv = new Ajv2020({ allErrors: true, strict: false }); + for (const descriptor of PHASE7_SEMANTIC_TOOL_CATALOG) { + this.#validators.set(descriptor.operationId, ajv.compile(descriptor.inputSchema)); + } + } + + listTools(runId: string): readonly Phase7SemanticToolDefinition[] { + const policyContext = this.#policyContext(runId); + const definitions: Phase7SemanticToolDefinition[] = []; + for (const descriptor of PHASE7_SEMANTIC_TOOL_CATALOG) { + const decision = decidePhase7SemanticAuthorization( + descriptor, + policyContext, + "exposure", + ); + this.#record(policyContext, decision, null, null, null); + if (decision.allowed) definitions.push(toDefinition(descriptor)); + } + return deepFreeze(definitions); + } + + authorizationRecords(): readonly Phase7SemanticAuthorizationRecord[] { + return deepFreeze(structuredClone(this.#records)); + } + + async dispatch(call: Phase7SemanticToolCall): Promise { + const descriptor = phase7SemanticToolDescriptor(call.operationId); + if (descriptor === undefined) { + return this.#unknownToolDenial(call); + } + + const policyContext = this.#policyContext(call.runId); + const invocation = decidePhase7SemanticAuthorization( + descriptor, + policyContext, + "invocation", + call.input, + ); + if (!invocation.allowed) { + const result = this.#denial(call, descriptor.operationId, denialCode(invocation), invocation.reason); + this.#record(policyContext, invocation, call.callId, call.input, result); + return result; + } + + if (containsProtectedSemanticData(call.input)) { + const decision = deniedDecision( + invocation, + "protected_data_denied", + "Protected data is not accepted by semantic tools.", + ); + const result = this.#denial(call, descriptor.operationId, denialCode(decision), decision.reason); + this.#record(policyContext, decision, call.callId, call.input, result); + return result; + } + + const validator = this.#validators.get(descriptor.operationId); + if (validator === undefined || !validator(call.input)) { + const decision = deniedDecision( + invocation, + "input_invalid", + formatValidationError(validator), + ); + const result = this.#denial(call, descriptor.operationId, denialCode(decision), decision.reason); + this.#record(policyContext, decision, call.callId, call.input, result); + return result; + } + + try { + const executed = await this.#execute( + descriptor.operationId, + call.runId, + call.input as Record, + ); + const result = deepFreeze({ + ok: true, + operationId: descriptor.operationId, + callId: call.callId, + result: redactSemanticValue(executed.result), + stateRevision: executed.stateRevision, + } as const); + this.#record(policyContext, invocation, call.callId, call.input, result); + return result; + } catch (error) { + const failure = normalizeFailure(error); + const decision = deniedDecision(invocation, failure.code, failure.message); + const result = this.#denial( + call, + descriptor.operationId, + failure.code, + failure.message, + failure.retryable, + ); + this.#record(policyContext, decision, call.callId, call.input, result); + return result; + } + } + + #policyContext(runId: string): Phase7SemanticPolicyContext { + const context = this.port.context(runId); + const scenario = this.options.scenario ?? DEFAULT_PHASE7_SCENARIO_POLICY; + return { + ...createPhase7SemanticPolicyContext( + context, + scenario, + this.options.explicitClaims ?? context.capabilities, + ), + runId, + }; + } + + async #execute( + operationId: Phase7SemanticOperationId, + runId: string, + input: Record, + ): Promise { + const context = this.port.context(runId); + const state = this.port.snapshot(); + switch (operationId) { + case "get_task_context": + return readSuccess(state.revision, context); + case "get_task_history": { + const limit = optionalNumber(input.limit, 50); + return readSuccess(state.revision, { + comments: state.comments + .filter((comment) => comment.taskId === context.activeTask.id) + .slice(-limit), + }); + } + case "list_documents": + return readSuccess(state.revision, { + documents: state.documents + .filter((document) => document.taskId === context.activeTask.id) + .map((document) => ({ + id: document.id, + key: document.key, + title: document.title, + format: document.format, + latestRevisionId: document.latestRevisionId, + revisionCount: document.revisions.length, + })), + }); + case "read_document": { + const document = activeDocument(state.documents, context, requiredString(input.key)); + const revision = document.revisions.find( + (candidate) => candidate.id === document.latestRevisionId, + ); + if (revision === undefined) { + throw new SemanticDispatchFailure("operation_unavailable", "Document revision is missing."); + } + return readSuccess(state.revision, { document, revision }); + } + case "list_document_revisions": { + const document = activeDocument(state.documents, context, requiredString(input.key)); + const limit = optionalNumber(input.limit, 50); + return readSuccess(state.revision, { + documentId: document.id, + key: document.key, + revisions: document.revisions.slice(-limit), + }); + } + case "list_agents": + return readSuccess(state.revision, { + actors: state.actors.map(({ capabilityGrants: _claims, budgetId: _budget, ...actor }) => actor), + }); + case "get_agent": { + const actor = state.actors.find((candidate) => candidate.id === requiredString(input.actorId)); + if (actor === undefined) throw new SemanticDispatchFailure("operation_unavailable", "Mock actor not found."); + const { capabilityGrants: _claims, budgetId: _budget, ...profile } = actor; + return readSuccess(state.revision, { actor: profile }); + } + case "search_tasks": { + const query = optionalString(input.query)?.toLowerCase(); + const statuses = optionalStringArray(input.statuses); + const limit = optionalNumber(input.limit, 50); + const tasks = state.tasks.filter((task) => + (query === undefined || `${task.identifier} ${task.title} ${task.description ?? ""}`.toLowerCase().includes(query)) && + (statuses.length === 0 || statuses.includes(task.status)), + ).slice(0, limit); + return readSuccess(state.revision, { tasks }); + } + case "list_approvals": + return readSuccess(state.revision, { approvals: state.approvals }); + case "get_approval": { + const approval = approvalById(state.approvals, requiredString(input.approvalId)); + return readSuccess(state.revision, { approval }); + } + case "get_approval_context": { + const approval = approvalById(state.approvals, requiredString(input.approvalId)); + return readSuccess(state.revision, { + approval, + tasks: state.tasks.filter((task) => approval.taskIds.includes(task.id)), + }); + } + case "get_workspace_runtime": + return readSuccess(state.revision, { + services: state.workspaceServices.filter( + (service) => service.taskId === context.activeTask.id, + ), + }); + case "generic_api_request": + return this.#genericApiRead(state.revision, context, input); + default: + return this.#applyMutation(operationId, runId, context, input); + } + } + + #genericApiRead( + revision: number, + context: Phase7RunContext, + input: Record, + ): OperationSuccess { + if (input.method === "GET" && input.path === "/mock/state/revision") { + return readSuccess(revision, { revision }); + } + if (input.method === "GET" && input.path === "/mock/task") { + return readSuccess(revision, { task: context.activeTask }); + } + throw new SemanticDispatchFailure( + "operation_unavailable", + "The test-only generic request is outside the explicit mock allowlist.", + ); + } + + async #applyMutation( + operationId: Phase7SemanticOperationId, + runId: string, + context: Phase7RunContext, + input: Record, + ): Promise { + const idempotencyKey = requiredString(input.idempotencyKey); + const taskId = context.activeTask.id; + let command: Phase7SemanticCommand; + switch (operationId) { + case "report_progress": + case "answer_status_question": + command = { kind: "report_progress", taskId, body: requiredString(input.body) }; + break; + case "write_document": + command = { + kind: "write_document", + taskId, + key: requiredString(input.key), + title: requiredString(input.title), + body: requiredString(input.body), + baseRevisionId: nullableString(input.baseRevisionId), + changeSummary: nullableOptionalString(input.changeSummary), + }; + break; + case "request_human_input": + command = { + kind: "request_human_input", + taskId, + interactionKind: requiredString(input.interactionKind) as Extract["interactionKind"], + title: requiredString(input.title), + prompt: requiredString(input.prompt), + payload: optionalJson(input.payload), + targetRevisionId: nullableOptionalString(input.targetRevisionId), + continuationPolicy: requiredString(input.continuationPolicy) as Extract["continuationPolicy"], + }; + break; + case "register_deliverable": + command = { + kind: "register_deliverable", + taskId, + filename: requiredString(input.filename), + contentType: requiredString(input.contentType), + byteSize: requiredNumber(input.byteSize), + sha256: requiredString(input.sha256), + contentRef: requiredString(input.contentRef), + title: requiredString(input.title), + }; + break; + case "finish_task": + command = { kind: "finish_task", taskId, summary: requiredString(input.summary) }; + break; + case "block_task": + command = { kind: "block_task", taskId, reason: requiredString(input.reason), blockedByTaskIds: optionalStringArray(input.blockedByTaskIds) }; + break; + case "request_review": + command = { kind: "request_review", taskId, summary: requiredString(input.summary) }; + break; + case "set_dependencies": + command = { kind: "set_dependencies", taskId, blockedByTaskIds: optionalStringArray(input.blockedByTaskIds) }; + break; + case "create_task": + command = { + kind: "create_task", + taskId, + title: requiredString(input.title), + description: nullableOptionalString(input.description), + assigneeActorId: nullableOptionalString(input.assigneeActorId), + priority: optionalString(input.priority) as Extract["priority"], + blockedByTaskIds: optionalStringArray(input.blockedByTaskIds), + }; + break; + case "request_approval": + command = { kind: "request_approval", taskId, approvalType: requiredString(input.approvalType), payload: optionalJson(input.payload) }; + break; + case "decide_approval": + command = { kind: "decide_approval", taskId, approvalId: requiredString(input.approvalId), decision: requiredString(input.decision) as Extract["decision"], note: requiredString(input.note) }; + break; + case "comment_on_approval": + command = { kind: "comment_on_approval", taskId, approvalId: requiredString(input.approvalId), body: requiredString(input.body) }; + break; + case "control_workspace_service": + command = { kind: "control_workspace_service", taskId, serviceId: requiredString(input.serviceId), action: requiredString(input.action) as Extract["action"], url: nullableOptionalString(input.url) }; + break; + case "schedule_wake": + command = { kind: "schedule_wake", taskId, reason: requiredString(input.reason) as Extract["reason"], payload: optionalJson(input.payload), delayTicks: requiredNumber(input.delayTicks) }; + break; + default: + throw new SemanticDispatchFailure("operation_unavailable", "No mock operation is bound to this descriptor."); + } + const outcome = await this.port.tryApplyCommand({ runId, idempotencyKey, command }); + return commandOutcome(outcome); + } + + #record( + context: Phase7SemanticPolicyContext, + decision: Phase7SemanticAuthorizationDecision, + callId: string | null, + input: unknown, + result: unknown, + ): void { + this.#recordCounter += 1; + this.#records.push(deepFreeze({ + schema: "paperclip.semantic-authorization-record.v1", + id: `semantic-auth-${String(this.#recordCounter).padStart(6, "0")}`, + runId: context.runId, + scenarioId: context.scenario.id, + actorId: context.actor.id, + taskId: context.task.id, + callId, + ...decision, + input: input === null ? null : redactSemanticValue(input), + result: result === null ? null : redactSemanticValue(result), + })); + } + + #unknownToolDenial(call: Phase7SemanticToolCall): Phase7SemanticToolResult { + return deepFreeze({ + ok: false, + operationId: call.operationId, + callId: call.callId, + denial: { + schema: "paperclip.semantic-denial.v1", + code: "tool_not_exposed", + message: "The requested semantic tool is not in the stable catalog.", + retryable: false, + }, + stateRevision: this.port.snapshot().revision, + }); + } + + #denial( + call: Phase7SemanticToolCall, + operationId: Phase7SemanticOperationId, + code: Exclude, + message: string, + retryable = false, + ): Phase7SemanticToolResult { + return deepFreeze({ + ok: false, + operationId, + callId: call.callId, + denial: { + schema: "paperclip.semantic-denial.v1", + code, + message: redactSemanticValue(message) as string, + retryable, + }, + stateRevision: this.port.snapshot().revision, + }); + } +} + +function commandOutcome(outcome: Phase7CommandOutcome): OperationSuccess { + if (!outcome.ok) { + throw new SemanticDispatchFailure( + "control_plane_denied", + outcome.error.message, + outcome.error.retryable, + ); + } + return { result: outcome.result as unknown as Phase7JsonValue, stateRevision: outcome.result.stateRevision }; +} + +function toDefinition(descriptor: Phase7SemanticToolDescriptor): Phase7SemanticToolDefinition { + return { + name: descriptor.operationId, + description: descriptor.description, + inputSchema: descriptor.inputSchema, + outputSchema: descriptor.outputSchema, + annotations: { + semanticContract: descriptor.schema, + operationId: descriptor.operationId, + version: descriptor.version, + exposure: descriptor.exposure, + requiredClaims: descriptor.requiredClaims, + }, + }; +} + +function deniedDecision( + allowed: Phase7SemanticAuthorizationDecision, + code: Phase7SemanticDenialCode, + reason: string, +): Phase7SemanticAuthorizationDecision { + return { ...allowed, allowed: false, code, reason }; +} + +function denialCode( + decision: Phase7SemanticAuthorizationDecision, +): Phase7SemanticDenialCode { + if (decision.code === "allowed") { + throw new Error("allowed authorization decision cannot create a denial"); + } + return decision.code; +} + +function normalizeFailure(error: unknown): SemanticDispatchFailure { + return error instanceof SemanticDispatchFailure + ? error + : new SemanticDispatchFailure("operation_unavailable", "The semantic operation failed safely."); +} + +function formatValidationError(validator: ValidateFunction | undefined): string { + const issue = validator?.errors?.[0]; + if (issue === undefined) return "Semantic tool input does not match its stable schema."; + return `Semantic tool input ${issue.instancePath || "/"} ${issue.message ?? "is invalid"}.`; +} + +function activeDocument( + documents: ReturnType["documents"], + context: Phase7RunContext, + key: string, +) { + const document = documents.find( + (candidate) => candidate.taskId === context.activeTask.id && candidate.key === key, + ); + if (document === undefined) throw new SemanticDispatchFailure("operation_unavailable", "Document not found."); + return document; +} + +function approvalById( + approvals: ReturnType["approvals"], + approvalId: string, +) { + const approval = approvals.find((candidate) => candidate.id === approvalId); + if (approval === undefined) throw new SemanticDispatchFailure("operation_unavailable", "Approval not found."); + return approval; +} + +function readSuccess(revision: number, value: unknown): OperationSuccess { + return { result: redactSemanticValue(value), stateRevision: revision }; +} + +function requiredString(value: unknown): string { + if (typeof value !== "string") throw new SemanticDispatchFailure("input_invalid", "Expected string input."); + return value; +} + +function optionalString(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined; +} + +function nullableString(value: unknown): string | null { + return value === null ? null : requiredString(value); +} + +function nullableOptionalString(value: unknown): string | null | undefined { + return value === undefined ? undefined : nullableString(value); +} + +function requiredNumber(value: unknown): number { + if (typeof value !== "number") throw new SemanticDispatchFailure("input_invalid", "Expected number input."); + return value; +} + +function optionalNumber(value: unknown, fallback: number): number { + return typeof value === "number" ? value : fallback; +} + +function optionalStringArray(value: unknown): string[] { + return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : []; +} + +function optionalJson(value: unknown): Phase7JsonValue { + return value === undefined ? {} : redactSemanticValue(value); +} + +function deepFreeze(value: T): T { + if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value; + Object.freeze(value); + for (const child of Object.values(value)) deepFreeze(child); + return value; +} diff --git a/packages/paperclip-runner/src/semantic-tools/index.ts b/packages/paperclip-runner/src/semantic-tools/index.ts new file mode 100644 index 00000000000..c0a3c1d1fb7 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/index.ts @@ -0,0 +1,6 @@ +export * from "./types.js"; +export * from "./catalog.js"; +export * from "./policy.js"; +export * from "./redaction.js"; +export * from "./provider-neutral.js"; +export * from "./dispatcher.js"; diff --git a/packages/paperclip-runner/src/semantic-tools/policy.ts b/packages/paperclip-runner/src/semantic-tools/policy.ts new file mode 100644 index 00000000000..c10189861c8 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/policy.ts @@ -0,0 +1,153 @@ +import type { Phase7RunContext } from "../mock-core/phase7-control-plane-types.js"; +import { PHASE7_SEMANTIC_TOOL_CATALOG } from "./catalog.js"; +import type { + Phase7SemanticAuthorizationDecision, + Phase7SemanticOperationId, + Phase7SemanticPolicyContext, + Phase7SemanticScenarioPolicy, + Phase7SemanticToolDescriptor, +} from "./types.js"; + +const READ_OPERATIONS = new Set([ + "get_task_context", + "get_task_history", + "list_documents", + "read_document", + "list_document_revisions", + "list_agents", + "get_agent", + "search_tasks", + "list_approvals", + "get_approval", + "get_approval_context", + "get_workspace_runtime", +]); + +export const DEFAULT_PHASE7_SCENARIO_POLICY: Phase7SemanticScenarioPolicy = { + id: "phase7-default", +}; + +export function createPhase7SemanticPolicyContext( + context: Phase7RunContext, + scenario: Phase7SemanticScenarioPolicy = DEFAULT_PHASE7_SCENARIO_POLICY, + explicitClaims: readonly string[] = context.capabilities, +): Phase7SemanticPolicyContext { + return deepFreeze({ + runId: context.activeTask.executionRunId ?? context.activeTask.checkoutRunId ?? "", + actor: context.actor, + task: context.activeTask, + runClaims: sortedUnique(context.capabilities), + explicitClaims: sortedUnique(explicitClaims), + scenario, + }); +} + +export function decidePhase7SemanticAuthorization( + descriptor: Phase7SemanticToolDescriptor, + context: Phase7SemanticPolicyContext, + phase: "exposure" | "invocation", + input?: unknown, +): Phase7SemanticAuthorizationDecision { + const effectiveClaims = computeEffectiveClaims(context); + const base = { phase, operationId: descriptor.operationId, effectiveClaims } as const; + const deny = ( + code: Exclude, + reason: string, + ): Phase7SemanticAuthorizationDecision => ({ ...base, allowed: false, code, reason }); + + if (context.actor.status !== "active") { + return deny("actor_inactive", "The mock actor is not active."); + } + if (!descriptor.allowedModes.includes(context.task.workMode)) { + return deny("task_mode_denied", "The semantic operation is unavailable in this task mode."); + } + if ( + context.scenario.denyOperations?.includes(descriptor.operationId) === true || + (descriptor.exposure === "optional" && + context.scenario.allowOperations !== undefined && + !context.scenario.allowOperations.includes(descriptor.operationId)) + ) { + return deny("scenario_denied", "The active scenario does not permit this semantic operation."); + } + if (descriptor.disabledByDefault === true) { + if ( + descriptor.operationId !== "generic_api_request" || + context.scenario.enableGenericApiRequest !== true + ) { + return deny("generic_api_disabled", "The generic API escape hatch is disabled."); + } + } + if ( + descriptor.allowedRoles !== undefined && + !descriptor.allowedRoles.includes(context.actor.role.trim().toLowerCase()) + ) { + return deny("actor_role_denied", "The actor role cannot invoke this semantic operation."); + } + if (descriptor.requiredClaims.some((claim) => !effectiveClaims.includes(claim))) { + return deny("required_claim_missing", "The run lacks an explicit claim required by this operation."); + } + if ( + phase === "invocation" && + (context.task.assigneeActorId !== context.actor.id || + context.task.executionRunId !== context.runId || + context.task.checkoutRunId !== context.runId) + ) { + return deny("task_ownership_denied", "The run no longer owns the active task."); + } + if ( + phase === "invocation" && + !READ_OPERATIONS.has(descriptor.operationId) && + ["done", "cancelled"].includes(context.task.status) + ) { + return deny("task_state_denied", "The active task is already terminal."); + } + if (descriptor.operationId === "request_human_input" && input !== undefined) { + const interactionKind = readStringProperty(input, "interactionKind"); + if ( + interactionKind !== undefined && + context.scenario.allowedInteractionKinds !== undefined && + !context.scenario.allowedInteractionKinds.includes( + interactionKind as Parameters["includes"]>[0], + ) + ) { + return deny("interaction_kind_denied", "The scenario does not allow this interaction kind."); + } + } + return { ...base, allowed: true, code: "allowed", reason: "Explicit semantic policy allowed the operation." }; +} + +export function exposedPhase7SemanticDescriptors( + context: Phase7SemanticPolicyContext, +): readonly Phase7SemanticToolDescriptor[] { + return PHASE7_SEMANTIC_TOOL_CATALOG.filter( + (descriptor) => decidePhase7SemanticAuthorization(descriptor, context, "exposure").allowed, + ); +} + +function computeEffectiveClaims(context: Phase7SemanticPolicyContext): string[] { + const granted = new Set([ + ...context.actor.capabilityGrants, + ...(context.scenario.claims ?? []), + ]); + const explicitlyDelegated = new Set(context.explicitClaims); + return sortedUnique( + context.runClaims.filter((claim) => granted.has(claim) && explicitlyDelegated.has(claim)), + ); +} + +function readStringProperty(input: unknown, key: string): string | undefined { + if (typeof input !== "object" || input === null || Array.isArray(input)) return undefined; + const value = (input as Record)[key]; + return typeof value === "string" ? value : undefined; +} + +function sortedUnique(values: readonly string[]): string[] { + return [...new Set(values)].sort((left, right) => left.localeCompare(right)); +} + +function deepFreeze(value: T): T { + if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value; + Object.freeze(value); + for (const child of Object.values(value)) deepFreeze(child); + return value; +} diff --git a/packages/paperclip-runner/src/semantic-tools/provider-neutral.ts b/packages/paperclip-runner/src/semantic-tools/provider-neutral.ts new file mode 100644 index 00000000000..0d3fe30d286 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/provider-neutral.ts @@ -0,0 +1,69 @@ +import { PHASE7_SEMANTIC_TOOL_CATALOG } from "./catalog.js"; +import type { + Phase7SemanticToolDefinition, + Phase7SemanticToolDescriptor, +} from "./types.js"; + +export type Phase7SemanticBindingKind = "fake" | "live_codex"; + +export interface Phase7ProviderNeutralSemanticBinding { + readonly schema: "paperclip.semantic-binding.v1"; + readonly bindingKind: Phase7SemanticBindingKind; + readonly contracts: readonly Phase7SemanticToolDefinition[]; +} + +/** + * Converts transport-neutral descriptors into model-provider contracts. The + * binding label is deliberately outside the contract array, so fake and live + * Codex bindings cannot drift in names, schemas, claims, or redaction metadata. + */ +export function createPhase7ProviderNeutralBinding( + bindingKind: Phase7SemanticBindingKind, + descriptors: readonly Phase7SemanticToolDescriptor[] = PHASE7_SEMANTIC_TOOL_CATALOG, +): Phase7ProviderNeutralSemanticBinding { + return deepFreeze({ + schema: "paperclip.semantic-binding.v1", + bindingKind, + contracts: descriptors.map(toDefinition), + }); +} + +export function phase7GeneratedSemanticContracts(): readonly Phase7SemanticToolDefinition[] { + return createPhase7ProviderNeutralBinding("fake").contracts; +} + +export function serializePhase7GeneratedSemanticContracts(): string { + return `${canonicalJson(phase7GeneratedSemanticContracts())}\n`; +} + +function toDefinition(descriptor: Phase7SemanticToolDescriptor): Phase7SemanticToolDefinition { + return { + name: descriptor.operationId, + description: descriptor.description, + inputSchema: descriptor.inputSchema, + outputSchema: descriptor.outputSchema, + annotations: { + semanticContract: descriptor.schema, + operationId: descriptor.operationId, + version: descriptor.version, + exposure: descriptor.exposure, + requiredClaims: descriptor.requiredClaims, + }, + }; +} + +function deepFreeze(value: T): T { + if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value; + Object.freeze(value); + for (const child of Object.values(value)) deepFreeze(child); + return value; +} + +function canonicalJson(value: unknown): string { + if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`; + if (typeof value === "object" && value !== null) { + const record = value as Record; + return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`; + } + return JSON.stringify(value) ?? "null"; +} diff --git a/packages/paperclip-runner/src/semantic-tools/redaction.ts b/packages/paperclip-runner/src/semantic-tools/redaction.ts new file mode 100644 index 00000000000..9ab6c65a79a --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/redaction.ts @@ -0,0 +1,46 @@ +import type { Phase7JsonValue } from "../mock-core/phase7-control-plane-types.js"; + +const SENSITIVE_KEY = /(?:authorization|cookie|credential|password|passwd|private.?key|secret|token|api.?key|connection.?string)/i; +const SECRET_VALUE = /(?:\bBearer\s+[A-Za-z0-9._~+\/-]+=*|\b(?:sk|pk|pcgw|ghp|github_pat)_[A-Za-z0-9_-]{8,}|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,})/i; +const SECRET_QUERY_DETECT = /[?&](?:code|key|secret|state|token|api[_-]?key|access[_-]?token)=[^&#\s]+/i; +const SECRET_QUERY = /([?&](?:code|key|secret|state|token|api[_-]?key|access[_-]?token)=)[^&#\s]+/gi; + +export const PHASE7_REDACTED = "[REDACTED]"; + +export function containsProtectedSemanticData(value: unknown, key = ""): boolean { + if (SENSITIVE_KEY.test(key)) return true; + if (typeof value === "string") { + return SECRET_VALUE.test(value) || SECRET_QUERY_DETECT.test(value); + } + if (Array.isArray(value)) { + return value.some((child) => containsProtectedSemanticData(child)); + } + if (typeof value === "object" && value !== null) { + return Object.entries(value).some(([childKey, child]) => + containsProtectedSemanticData(child, childKey), + ); + } + return false; +} + +export function redactSemanticValue(value: unknown, key = ""): Phase7JsonValue { + if (SENSITIVE_KEY.test(key)) return PHASE7_REDACTED; + if (typeof value === "string") { + return value + .replace(SECRET_VALUE, PHASE7_REDACTED) + .replace(SECRET_QUERY, `$1${PHASE7_REDACTED}`); + } + if (Array.isArray(value)) return value.map((child) => redactSemanticValue(child)); + if (typeof value === "object" && value !== null) { + return Object.fromEntries( + Object.entries(value).map(([childKey, child]) => [ + childKey, + redactSemanticValue(child, childKey), + ]), + ); + } + if (typeof value === "number" || typeof value === "boolean" || value === null) { + return value; + } + return String(value); +} diff --git a/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts b/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts new file mode 100644 index 00000000000..98e3a580649 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts @@ -0,0 +1,287 @@ +import { describe, expect, it } from "vitest"; + +import type { Phase7FixtureSeed } from "../mock-core/phase7-control-plane-types.js"; +import { Phase7MockControlPlaneAdapter } from "../mock-core/phase7-mock-control-plane-adapter.js"; +import { PHASE7_SEMANTIC_TOOL_CATALOG } from "./catalog.js"; +import { Phase7SemanticDispatcher } from "./dispatcher.js"; +import { createPhase7ProviderNeutralBinding } from "./provider-neutral.js"; + +const OPEN = { + identity: { + runId: "run-semantic-1", + sessionId: "session-semantic-1", + companyId: "company-1", + issueId: "task-1", + agentId: "actor-1", + }, + backendKind: "mock" as const, + sourceInstanceId: "semantic-test", +}; + +async function running( + claims: string[] = [], + seed: Phase7FixtureSeed = {}, +) { + const adapter = new Phase7MockControlPlaneAdapter({ + ...seed, + actors: seed.actors ?? [ + { + id: "actor-1", + companyId: "company-1", + name: "Semantic Engineer", + role: "engineer", + status: "active", + budgetId: "budget-actor-1", + capabilityGrants: claims, + }, + ], + }); + await adapter.start(); + await adapter.openFixtureRun({ ...OPEN, capabilities: claims }); + return adapter; +} + +describe("Phase 7 semantic catalog and authorization", () => { + it("publishes a stable narrow catalog without credentials or control-plane-owned tools", () => { + const names = PHASE7_SEMANTIC_TOOL_CATALOG.map((tool) => tool.operationId); + expect(new Set(names).size).toBe(names.length); + expect(names).toHaveLength(28); + expect(names).toContain("get_task_context"); + expect(names).toContain("finish_task"); + expect(names).not.toContain("checkout_task"); + expect(names).not.toContain("record_budget_usage"); + expect(names).not.toContain("resolve_human_input"); + expect(PHASE7_SEMANTIC_TOOL_CATALOG.find((tool) => tool.operationId === "generic_api_request")) + .toMatchObject({ exposure: "optional", disabledByDefault: true }); + const schemaKeys = PHASE7_SEMANTIC_TOOL_CATALOG.flatMap((tool) => + Object.keys(tool.inputSchema.properties ?? {}), + ); + expect(schemaKeys.join(" ")).not.toMatch( + /authorization|password|credential|api.?key|access.?token|secret/i, + ); + }); + + it("exposes always tools while optional tools require actor, scenario, and explicit run claims", async () => { + const adapter = await running(); + const dispatcher = new Phase7SemanticDispatcher(adapter); + const names = dispatcher.listTools(OPEN.identity.runId).map((tool) => tool.name); + expect(names).toContain("get_task_context"); + expect(names).toContain("report_progress"); + expect(names).not.toContain("set_dependencies"); + expect(names).not.toContain("generic_api_request"); + + const before = adapter.snapshot().revision; + const denied = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "denied-dependencies", + operationId: "set_dependencies", + input: { idempotencyKey: "deps-1", blockedByTaskIds: [] }, + }); + expect(denied).toMatchObject({ + ok: false, + denial: { code: "required_claim_missing", retryable: false }, + }); + expect(adapter.snapshot().revision).toBe(before); + }); + + it("executes a granted optional operation through the mock port", async () => { + const claim = "dependencies:write"; + const adapter = await running([claim], { + tasks: [ + { + id: "task-1", + companyId: "company-1", + identifier: "MCK-1", + title: "Active task", + description: null, + status: "todo", + priority: "medium", + workMode: "standard", + parentId: null, + assigneeActorId: "actor-1", + checkoutRunId: null, + executionRunId: null, + startedAt: null, + completedAt: null, + }, + { + id: "task-2", + companyId: "company-1", + identifier: "MCK-2", + title: "Blocking task", + description: null, + status: "todo", + priority: "high", + workMode: "standard", + parentId: null, + assigneeActorId: null, + checkoutRunId: null, + executionRunId: null, + startedAt: null, + completedAt: null, + }, + ], + }); + const dispatcher = new Phase7SemanticDispatcher(adapter, { + scenario: { id: "dependency-manager", claims: [claim] }, + explicitClaims: [claim], + }); + expect(dispatcher.listTools(OPEN.identity.runId).map((tool) => tool.name)) + .toContain("set_dependencies"); + const result = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "set-dependencies", + operationId: "set_dependencies", + input: { idempotencyKey: "deps-1", blockedByTaskIds: ["task-2"] }, + }); + expect(result).toMatchObject({ ok: true, result: { disposition: "applied" } }); + expect(adapter.snapshot().blockers).toEqual([ + expect.objectContaining({ taskId: "task-1", blockedByTaskId: "task-2" }), + ]); + }); + + it("rechecks policy immediately before every operation", async () => { + const claim = "dependencies:write"; + const adapter = await running([claim]); + let exposeClaim = true; + const port = { + snapshot: () => adapter.snapshot(), + tryApplyCommand: adapter.tryApplyCommand.bind(adapter), + context: (runId: string) => { + const context = structuredClone(adapter.context(runId)); + context.capabilities = exposeClaim ? [claim] : []; + return context; + }, + }; + const dispatcher = new Phase7SemanticDispatcher(port, { + scenario: { id: "revocation", claims: [claim] }, + explicitClaims: [claim], + }); + expect(dispatcher.listTools(OPEN.identity.runId).map((tool) => tool.name)) + .toContain("set_dependencies"); + exposeClaim = false; + const before = adapter.snapshot().revision; + const result = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "revoked-call", + operationId: "set_dependencies", + input: { idempotencyKey: "revoked-1", blockedByTaskIds: [] }, + }); + expect(result).toMatchObject({ ok: false, denial: { code: "required_claim_missing" } }); + expect(adapter.snapshot().revision).toBe(before); + }); + + it("returns typed denials for absent, mode-denied, and generic escape-hatch tools", async () => { + const adapter = await running([], { + tasks: [{ + id: "task-1", + companyId: "company-1", + identifier: "MCK-1", + title: "Ask-only task", + description: null, + status: "todo", + priority: "medium", + workMode: "ask", + parentId: null, + assigneeActorId: "actor-1", + checkoutRunId: null, + executionRunId: null, + startedAt: null, + completedAt: null, + }], + }); + const dispatcher = new Phase7SemanticDispatcher(adapter); + const names = dispatcher.listTools(OPEN.identity.runId).map((tool) => tool.name); + expect(names).not.toContain("write_document"); + expect(names).not.toContain("finish_task"); + await expect(dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "unknown", + operationId: "paperclipApiRequest", + input: {}, + })).resolves.toMatchObject({ ok: false, denial: { code: "tool_not_exposed" } }); + await expect(dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "mode-denied", + operationId: "write_document", + input: {}, + })).resolves.toMatchObject({ ok: false, denial: { code: "task_mode_denied" } }); + }); + + it("rejects protected inputs before mutation and redacts authorization records", async () => { + const claim = "governance:approvals:request"; + const adapter = await running([claim]); + const dispatcher = new Phase7SemanticDispatcher(adapter, { + scenario: { id: "approval", claims: [claim] }, + explicitClaims: [claim], + }); + const secret = "Bearer secret-value-123456"; + const before = adapter.snapshot().revision; + const result = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "protected-approval", + operationId: "request_approval", + input: { + idempotencyKey: "approval-secret", + approvalType: "request_board_approval", + payload: { authorization: secret }, + }, + }); + expect(result).toMatchObject({ ok: false, denial: { code: "protected_data_denied" } }); + expect(adapter.snapshot().revision).toBe(before); + const serialized = JSON.stringify(dispatcher.authorizationRecords()); + expect(serialized).not.toContain(secret); + expect(serialized).toContain("[REDACTED]"); + }); + + it("redacts protected mock state from read results", async () => { + const secret = "Bearer mock-secret-123456"; + const adapter = await running([], { + comments: [{ + id: "comment-secret", + taskId: "task-1", + authorActorId: "actor-1", + body: `diagnostic ${secret}`, + createdAt: "2026-08-09T00:00:00.000Z", + }], + }); + const dispatcher = new Phase7SemanticDispatcher(adapter); + const result = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "history", + operationId: "get_task_history", + input: {}, + }); + expect(JSON.stringify(result)).not.toContain(secret); + expect(JSON.stringify(result)).toContain("[REDACTED]"); + }); + + it("applies always-tool commands and records authorization for every invocation", async () => { + const adapter = await running(); + const dispatcher = new Phase7SemanticDispatcher(adapter); + const progress = await dispatcher.dispatch({ + runId: OPEN.identity.runId, + callId: "progress", + operationId: "report_progress", + input: { idempotencyKey: "progress-1", body: "Semantic catalog is active." }, + }); + expect(progress).toMatchObject({ ok: true }); + expect(adapter.snapshot().comments).toEqual([ + expect.objectContaining({ body: "Semantic catalog is active." }), + ]); + const records = dispatcher.authorizationRecords().filter((record) => record.phase === "invocation"); + expect(records).toHaveLength(1); + expect(records[0]).toMatchObject({ allowed: true, operationId: "report_progress", callId: "progress" }); + }); + + it("keeps generated contracts identical across fake and live Codex bindings", () => { + const fake = createPhase7ProviderNeutralBinding("fake"); + const live = createPhase7ProviderNeutralBinding("live_codex"); + expect(fake.bindingKind).toBe("fake"); + expect(live.bindingKind).toBe("live_codex"); + expect(live.contracts).toEqual(fake.contracts); + expect(live.contracts.map((tool) => tool.name)).toEqual( + PHASE7_SEMANTIC_TOOL_CATALOG.map((tool) => tool.operationId), + ); + }); +}); diff --git a/packages/paperclip-runner/src/semantic-tools/types.ts b/packages/paperclip-runner/src/semantic-tools/types.ts new file mode 100644 index 00000000000..252493781a1 --- /dev/null +++ b/packages/paperclip-runner/src/semantic-tools/types.ts @@ -0,0 +1,182 @@ +import type { + Phase7InteractionKind, + Phase7JsonValue, + Phase7RunContext, +} from "../mock-core/phase7-control-plane-types.js"; + +export type Phase7SemanticToolExposure = "always" | "optional"; + +export type Phase7SemanticOperationId = + | "get_task_context" + | "get_task_history" + | "list_documents" + | "read_document" + | "list_document_revisions" + | "report_progress" + | "answer_status_question" + | "write_document" + | "request_human_input" + | "register_deliverable" + | "finish_task" + | "block_task" + | "request_review" + | "list_agents" + | "get_agent" + | "search_tasks" + | "list_approvals" + | "get_approval" + | "get_approval_context" + | "get_workspace_runtime" + | "control_workspace_service" + | "set_dependencies" + | "create_task" + | "request_approval" + | "decide_approval" + | "comment_on_approval" + | "schedule_wake" + | "generic_api_request"; + +export interface Phase7JsonSchema { + readonly $schema?: string; + readonly type?: string | readonly string[]; + readonly title?: string; + readonly description?: string; + readonly properties?: Readonly>; + readonly required?: readonly string[]; + readonly additionalProperties?: boolean | Phase7JsonSchema; + readonly items?: Phase7JsonSchema; + readonly enum?: readonly Phase7JsonValue[]; + readonly const?: Phase7JsonValue; + readonly oneOf?: readonly Phase7JsonSchema[]; + readonly anyOf?: readonly Phase7JsonSchema[]; + readonly minimum?: number; + readonly maximum?: number; + readonly minLength?: number; + readonly maxLength?: number; + readonly minItems?: number; + readonly maxItems?: number; + readonly uniqueItems?: boolean; + readonly pattern?: string; + readonly format?: string; + readonly default?: Phase7JsonValue; +} + +export interface Phase7SemanticToolDescriptor { + readonly schema: "paperclip.semantic-tool.v1"; + readonly operationId: Phase7SemanticOperationId; + readonly version: 1; + readonly title: string; + readonly description: string; + readonly exposure: Phase7SemanticToolExposure; + readonly requiredClaims: readonly string[]; + readonly allowedModes: readonly Phase7RunContext["activeTask"]["workMode"][]; + readonly allowedRoles?: readonly string[]; + readonly disabledByDefault?: boolean; + readonly inputSchema: Phase7JsonSchema; + readonly outputSchema: Phase7JsonSchema; +} + +export interface Phase7SemanticScenarioPolicy { + readonly id: string; + /** Claims that this scenario is permitted to grant. Run claims still narrow this set. */ + readonly claims?: readonly string[]; + readonly allowOperations?: readonly Phase7SemanticOperationId[]; + readonly denyOperations?: readonly Phase7SemanticOperationId[]; + readonly allowedInteractionKinds?: readonly Phase7InteractionKind[]; + readonly enableGenericApiRequest?: boolean; +} + +export interface Phase7SemanticPolicyContext { + readonly runId: string; + readonly actor: Phase7RunContext["actor"]; + readonly task: Phase7RunContext["activeTask"]; + readonly runClaims: readonly string[]; + readonly explicitClaims: readonly string[]; + readonly scenario: Phase7SemanticScenarioPolicy; +} + +export type Phase7AuthorizationPhase = "exposure" | "invocation"; + +export type Phase7SemanticDenialCode = + | "tool_not_exposed" + | "actor_inactive" + | "task_mode_denied" + | "task_state_denied" + | "task_ownership_denied" + | "required_claim_missing" + | "actor_role_denied" + | "scenario_denied" + | "interaction_kind_denied" + | "generic_api_disabled" + | "input_invalid" + | "protected_data_denied" + | "operation_unavailable" + | "control_plane_denied"; + +export interface Phase7SemanticAuthorizationDecision { + readonly allowed: boolean; + readonly phase: Phase7AuthorizationPhase; + readonly operationId: Phase7SemanticOperationId; + readonly code: "allowed" | Phase7SemanticDenialCode; + readonly reason: string; + readonly effectiveClaims: readonly string[]; +} + +export interface Phase7SemanticAuthorizationRecord + extends Phase7SemanticAuthorizationDecision { + readonly schema: "paperclip.semantic-authorization-record.v1"; + readonly id: string; + readonly runId: string; + readonly scenarioId: string; + readonly actorId: string; + readonly taskId: string; + readonly callId: string | null; + readonly input: Phase7JsonValue | null; + readonly result: Phase7JsonValue | null; +} + +export interface Phase7SemanticToolDefinition { + readonly name: Phase7SemanticOperationId; + readonly description: string; + readonly inputSchema: Phase7JsonSchema; + readonly outputSchema: Phase7JsonSchema; + readonly annotations: { + readonly semanticContract: "paperclip.semantic-tool.v1"; + readonly operationId: Phase7SemanticOperationId; + readonly version: 1; + readonly exposure: Phase7SemanticToolExposure; + readonly requiredClaims: readonly string[]; + }; +} + +export interface Phase7SemanticToolCall { + readonly runId: string; + readonly callId: string; + readonly operationId: Phase7SemanticOperationId | string; + readonly input: unknown; +} + +export interface Phase7SemanticToolSuccess { + readonly ok: true; + readonly operationId: Phase7SemanticOperationId; + readonly callId: string; + readonly result: Phase7JsonValue; + readonly stateRevision: number; +} + +export interface Phase7SemanticToolDenial { + readonly ok: false; + readonly operationId: string; + readonly callId: string; + readonly denial: { + readonly schema: "paperclip.semantic-denial.v1"; + readonly code: Phase7SemanticDenialCode; + readonly message: string; + readonly retryable: boolean; + }; + readonly stateRevision: number; +} + +export type Phase7SemanticToolResult = + | Phase7SemanticToolSuccess + | Phase7SemanticToolDenial; diff --git a/packages/paperclip-runner/test/capability-contract.test.mjs b/packages/paperclip-runner/test/capability-contract.test.mjs new file mode 100644 index 00000000000..776655e8b66 --- /dev/null +++ b/packages/paperclip-runner/test/capability-contract.test.mjs @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import { resolve } from "node:path"; +import { validateRows } from "../scripts/generate-phase-07-contract.mjs"; + +const phaseDirectory = resolve(import.meta.dirname, "../generated/phase-07"); + +async function readRows(file) { + return JSON.parse(await readFile(resolve(phaseDirectory, file), "utf8")).rows; +} + +test("generated Phase 7 inventory has full source coverage", async () => { + const [capabilities, tools, evals] = await Promise.all([ + readRows("capabilities.yaml"), + readRows("mcp-tool-map.yaml"), + readRows("eval-traceability.yaml"), + ]); + + assert.equal(capabilities.length, 152); + assert.equal(tools.length, 41); + assert.equal(evals.length, 106); + assert.equal(new Set(evals.map((row) => row.group)).size, 16); + for (const row of [...capabilities, ...tools, ...evals]) { + assert.match(row.sourceAnchor, /\S/); + assert.match(row.semanticOperation, /\S/); + assert.match(row.expectedMockState, /\S/); + } +}); + +test("contract validation rejects missing, duplicate, and unclassified entries", () => { + const row = { + id: "example:1", + sourceAnchor: "source.md#L1:example", + primaryDisposition: "control_plane_owned", + }; + + assert.throws(() => validateRows([{ ...row, sourceAnchor: "" }], "fixture"), /source anchor/); + assert.throws(() => validateRows([row, { ...row, id: "example:2" }], "fixture"), /duplicate source anchor/); + assert.throws(() => validateRows([{ ...row, primaryDisposition: "unclassified" }], "fixture"), /valid primary disposition/); +});