diff --git a/packages/paperclip-runner/protocol/fixtures/evals/native-execution-seeded.json b/packages/paperclip-runner/protocol/fixtures/evals/native-execution-seeded.json
index aef9364cae..b70f0e356e 100644
--- a/packages/paperclip-runner/protocol/fixtures/evals/native-execution-seeded.json
+++ b/packages/paperclip-runner/protocol/fixtures/evals/native-execution-seeded.json
@@ -24,7 +24,7 @@
"prpVersion": 1,
"nativeExecutionVersion": 1,
"catalogVersion": 1,
- "catalogSha256": "sha256:842a1515a5b549fcc5df7675f3a96471b2f1ca33f4699cc5dd2ecf6c4235f2ec",
+ "catalogSha256": "sha256:f4d9e13a542b476b89c13793ae5c540406cd4cdc47941569beb22556de957dcc",
"driverContractVersion": 1,
"driverKind": "paperclip-deterministic",
"driverVersion": "1.0.0"
diff --git a/packages/paperclip-runner/protocol/manifest.json b/packages/paperclip-runner/protocol/manifest.json
index 954fc62fcf..21aa926027 100644
--- a/packages/paperclip-runner/protocol/manifest.json
+++ b/packages/paperclip-runner/protocol/manifest.json
@@ -160,7 +160,7 @@
},
{
"path": "fixtures/evals/native-execution-seeded.json",
- "sha256": "89641b73df452a5d03502bc151a81a68387ece129c8826e0572800c3b1c5265c",
+ "sha256": "1c0a3cb0e4be47b72506797cbcee920e51ab58185adc70135f54ecda76139a02",
"expectation": "accept",
"compatibilityCase": "canonical"
},
diff --git a/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs b/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs
index 2f80ec1eb3..684e5a2bef 100644
--- a/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs
+++ b/packages/paperclip-runner/scripts/generate-semantic-contracts.mjs
@@ -2,10 +2,16 @@ import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import { serializeCapabilityGeneratedSemanticContracts } from "../dist/semantic-tools/provider-neutral.js";
+import { PAPERCLIP_RUNNER_BUILD_METADATA } from "../dist/evals/build-metadata.js";
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const outputPath = resolve(packageRoot, "generated/capability/semantic-tool-contracts.json");
const generated = serializeCapabilityGeneratedSemanticContracts();
+// This is an explicitly seeded schema fixture, not retained live evidence.
+// Keep its advertised catalog identity synchronized with the shipped contracts.
+const fixturePath = resolve(packageRoot, "protocol/fixtures/evals/native-execution-seeded.json");
+const fixture = JSON.parse(await readFile(fixturePath, "utf8"));
+const fixtureCurrent = fixture.runner.catalogSha256 === PAPERCLIP_RUNNER_BUILD_METADATA.semanticCatalog.sha256;
if (process.argv.includes("--check")) {
const current = await readFile(outputPath, "utf8").catch(() => "");
@@ -13,7 +19,15 @@ if (process.argv.includes("--check")) {
process.stderr.write("semantic-tool-contracts.json is stale; run generate:semantic-contracts\n");
process.exitCode = 1;
}
+ if (!fixtureCurrent) {
+ process.stderr.write("native-execution-seeded.json catalog is stale; run generate:semantic-contracts and generate:protocol-manifest\n");
+ process.exitCode = 1;
+ }
} else {
await writeFile(outputPath, generated);
+ if (!fixtureCurrent) {
+ fixture.runner.catalogSha256 = PAPERCLIP_RUNNER_BUILD_METADATA.semanticCatalog.sha256;
+ await writeFile(fixturePath, `${JSON.stringify(fixture, null, 2)}\n`);
+ }
process.stdout.write(`wrote ${outputPath}\n`);
}
diff --git a/packages/paperclip-runner/spec/operation-groups/source.json b/packages/paperclip-runner/spec/operation-groups/source.json
index 19133ada3f..de088ece3a 100644
--- a/packages/paperclip-runner/spec/operation-groups/source.json
+++ b/packages/paperclip-runner/spec/operation-groups/source.json
@@ -50,6 +50,11 @@
"description": "Company-visible task, agent, project, and goal discovery.",
"operationIds": ["search_tasks", "list_agents", "get_agent", "list_projects", "list_goals"]
},
+ {
+ "id": "projects",
+ "description": "Project creation and authorized repository discovery through the live company/run authority.",
+ "operationIds": ["create_project", "list_project_repositories"]
+ },
{
"id": "delegation_dependencies",
"description": "Create delegated work and maintain dependency edges.",
@@ -163,12 +168,12 @@
"legacyGroup": 5,
"name": "Search",
"owner": "optional discovery tools",
- "operationIds": ["search_tasks", "list_agents", "get_agent", "list_projects", "list_goals"],
+ "operationIds": ["search_tasks", "list_agents", "get_agent", "list_projects", "list_goals", "list_project_repositories"],
"controlPlaneOperationIds": [],
"realSurface": "company issue search and agent/project/goal list/get routes",
"mockStateDomains": ["company", "task", "actor", "project", "goal"],
"prpEvidence": "bounded redacted read projections through tool-result item events",
- "gap": "Project and goal operations are scenario-only; every real service binding is unbound."
+ "gap": "Goal operations remain scenario-only. Project and repository discovery contracts are delivered by the live server authority; they are not implemented by the mock command dispatcher."
},
{
"id": "su",
@@ -259,12 +264,12 @@
"legacyGroup": 13,
"name": "Reference files",
"owner": "optional domain tools + test-only escape hatch",
- "operationIds": ["list_cases", "upsert_case", "list_routines", "manage_routine", "list_company_skills", "sync_company_skills", "list_secret_metadata", "read_secret_value", "export_company", "administer_company", "generic_api_request", "search_api", "call_api"],
+ "operationIds": ["list_cases", "upsert_case", "list_routines", "manage_routine", "list_company_skills", "sync_company_skills", "list_secret_metadata", "read_secret_value", "export_company", "administer_company", "generic_api_request", "search_api", "call_api", "create_project"],
"controlPlaneOperationIds": ["append_audit_record"],
- "realSurface": "case, routine, company-skill, secret, portability, and administration services",
+ "realSurface": "project, case, routine, company-skill, secret, portability, and administration services",
"mockStateDomains": ["company", "cases", "routines", "skills", "secrets", "audit", "fault"],
"prpEvidence": "bounded domain projections, redacted broker receipts, company diffs, and audit references",
- "gap": "These operations are scenario-only except generic_api_request, which is test-only; broad administer_company is deferred and cannot claim product coverage. Production escape-hatch and paired dedicated-tool regressions are recorded separately in paperclip-evals/evals/runner-api-tools; the legacy scenario count is not evidence of that coverage."
+ "gap": "Project creation and API tools require the live server authority; generic_api_request is test-only and the remaining domain operations are scenario-only. Broad administer_company is deferred and cannot claim product coverage. Production API and paired dedicated-tool regressions are recorded separately in paperclip-evals/evals/runner-api-tools; the legacy scenario count is not evidence of that coverage."
},
{
"id": "mh",
diff --git a/packages/paperclip-runner/spec/paperclip-agent-operation-groups.md b/packages/paperclip-runner/spec/paperclip-agent-operation-groups.md
index fb387a4759..8428c8c501 100644
--- a/packages/paperclip-runner/spec/paperclip-agent-operation-groups.md
+++ b/packages/paperclip-runner/spec/paperclip-agent-operation-groups.md
@@ -6,7 +6,7 @@ Status: canonical explanatory contract for the Paperclip runner V1 surface.
This document keeps three independent meanings of **group** separate. PRP families describe wire evidence and controller commands; capability placement decides who owns an operation; behavioral eval groups organize the 106 scenario corpus. None of the three axes can be used as a substitute for another.
-The generated totals are **105 PRP events in 31 event families**, **18 controller commands in 7 command families**, **10 control-plane operations**, **43 reconciled semantic operations** (14 always, 29 optional), and **106 scenarios in 16 behavior groups**.
+The generated totals are **105 PRP events in 31 event families**, **18 controller commands in 7 command families**, **10 control-plane operations**, **45 reconciled semantic operations** (14 always, 31 optional), and **106 scenarios in 16 behavior groups**.
## Axis 1: PRP v1 event and command families
@@ -87,13 +87,14 @@ Placement has exactly three outcomes:
`answer_status_question`, `block_task`, `finish_task`, `get_task_context`, `get_task_history`, `inspect_operation_result`, `list_document_revisions`, `list_documents`, `read_document`, `register_deliverable`, `report_progress`, `request_human_input`, `request_review`, `write_document`.
-### Optional operations (29) and grant groups (12)
+### Optional operations (31) and grant groups (13)
Grant groups are documentation/exposure bundles, not additional authority. The operation descriptor's exact `requiredClaims` remains decisive.
| Grant group | Operations | Required claims represented | Purpose |
| --- | --- | --- | --- |
| `discovery` | `search_tasks`
`list_agents`
`get_agent`
`list_projects`
`list_goals` | `discovery:agents:read`
`discovery:goals:read`
`discovery:projects:read`
`discovery:tasks:read` | Company-visible task, agent, project, and goal discovery. |
+| `projects` | `create_project`
`list_project_repositories` | none | Project creation and authorized repository discovery through the live company/run authority. |
| `delegation_dependencies` | `create_task`
`set_dependencies` | `delegation:tasks:create`
`dependencies:write` | Create delegated work and maintain dependency edges. |
| `governance` | `list_approvals`
`get_approval`
`get_approval_context`
`request_approval`
`decide_approval`
`comment_on_approval` | `governance:approvals:comment`
`governance:approvals:decide`
`governance:approvals:read`
`governance:approvals:request` | Read, request, comment on, and decide approvals under governed-action checks. |
| `cases` | `list_cases`
`upsert_case` | `cases:read`
`cases:write` | Read and update case summaries without reusing issue-document authority. |
@@ -118,6 +119,7 @@ Grant groups are documentation/exposure bundles, not additional authority. The o
| `call_api` | `optional_agent_tool` | `api:call` | `standard`
`ask`
`planning`
`skill_test` | `company_write` | `none` | no | inline/no mapping | `live`
`live_codex` | `PaperclipRunnerToolAuthority`
Authenticated PRP tool input/result and existing HTTP route authorization/activity records.
catalog PRP status: `bound` |
| `comment_on_approval` | `optional_agent_tool` | `governance:approvals:comment` | `standard`
`ask`
`planning`
`skill_test` | `governance` | `required` | no | `semantic_command:comment_on_approval` | `scenario` + `live`
`live_codex` | `unbound`
approval lifecycle plus governed-wait continuation and audit events
catalog PRP status: `audit_pending` |
| `control_workspace_service` | `optional_agent_tool` | `workspace:control` | `standard`
`skill_test` | `workspace_control` | `required` | no | `semantic_command:control_workspace_service` | `scenario` + `live`
`live_codex` | `unbound`
workspace service lifecycle event
catalog PRP status: `audit_pending` |
+| `create_project` | `optional_agent_tool` | none | `standard`
`skill_test` | `company_write` | `required` | no | inline/no mapping | `live`
`live_codex` | `PaperclipRunnerToolAuthority`
Authenticated project tools, persisted projects and repository workspaces, and run-bound activity.
catalog PRP status: `bound` |
| `create_task` | `optional_agent_tool` | `delegation:tasks:create` | `standard`
`skill_test` | `company_write` | `required` | no | `semantic_command:create_task` | `scenario` + `live`
`live_codex` | `issues.createChild`
semantic-operation item event plus company-entity state diff and audit record
catalog PRP status: `bound` |
| `decide_approval` | `optional_agent_tool` | `governance:approvals:decide` | `standard`
`skill_test`
roles: `board`
`approver`
`security` | `governance` | `required` | no | `semantic_command:decide_approval` | `scenario` + `live`
`live_codex` | `unbound`
approval lifecycle plus governed-wait continuation and audit events
catalog PRP status: `audit_pending` |
| `export_company` | `optional_agent_tool` | `portability:export` | `standard`
`skill_test` | `admin` | `required` | no | `mock_extension:portability.export` | `scenario`
`scenario_mock` | `unbound`
company admin/portability item event plus audit record
catalog PRP status: `audit_pending` |
@@ -137,7 +139,8 @@ Grant groups are documentation/exposure bundles, not additional authority. The o
| `list_document_revisions` | `always_agent_tool` | none | `standard`
`ask`
`planning`
`skill_test` | `read` | `none` | no | `snapshot_read:active_task_document_revisions` | `scenario` + `live`
`live_codex` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
| `list_documents` | `always_agent_tool` | none | `standard`
`ask`
`planning`
`skill_test` | `read` | `none` | no | `snapshot_read:active_task_documents` | `scenario` + `live`
`live_codex` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
| `list_goals` | `optional_agent_tool` | `discovery:goals:read` | `standard`
`skill_test` | `read` | `none` | no | `mock_extension:discovery.goals` | `scenario`
`scenario_mock` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
-| `list_projects` | `optional_agent_tool` | `discovery:projects:read` | `standard`
`skill_test` | `read` | `none` | no | `mock_extension:discovery.projects` | `scenario`
`scenario_mock` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
+| `list_project_repositories` | `optional_agent_tool` | none | `standard`
`ask`
`planning`
`skill_test` | `read` | `none` | no | inline/no mapping | `live`
`live_codex` | `PaperclipRunnerToolAuthority`
Authenticated project tools, persisted projects and repository workspaces, and run-bound activity.
catalog PRP status: `bound` |
+| `list_projects` | `optional_agent_tool` | `discovery:projects:read` | `standard`
`ask`
`planning`
`skill_test` | `read` | `none` | no | `mock_extension:discovery.projects` | `scenario` + `live`
`live_codex` | `PaperclipRunnerToolAuthority`
Authenticated project tools, persisted projects and repository workspaces, and run-bound activity.
catalog PRP status: `bound` |
| `list_routines` | `optional_agent_tool` | `routines:read` | `standard`
`skill_test` | `read` | `none` | no | `mock_extension:routines.list` | `scenario`
`scenario_mock` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
| `list_secret_metadata` | `optional_agent_tool` | `secrets:metadata:read` | `standard`
`skill_test` | `read` | `none` | no | `mock_extension:secrets.metadata` | `scenario`
`scenario_mock` | `unbound`
read projection surfaced via a tool-result item event; no control-plane state diff
catalog PRP status: `audit_pending` |
| `manage_routine` | `optional_agent_tool` | `routines:write` | `standard`
`skill_test` | `admin` | `required` | no | `mock_extension:routines.manage` | `scenario`
`scenario_mock` | `unbound`
company admin/portability item event plus audit record
catalog PRP status: `audit_pending` |
@@ -168,7 +171,7 @@ Behavior groups describe expected outcomes and trajectories. They do not grant t
| [`co` — Checkout](#behavior-group-co-checkout) | control plane | none | `checkout_task` | POST /api/issues/:id/checkout and execution-lock services | `task`
`actor`
`run`
`idempotency`
`fault` | 6 | run preparation and issue-status decision evidence with checkout receipt | Intentionally no model tool; the production checkout receipt still needs the additive semantic-receipt envelope. |
| [`st` — Status](#behavior-group-st-status) | always tools + control-plane arbitration | `answer_status_question`
`finish_task`
`block_task`
`request_review` | `reconcile_run`
`append_audit_record` | issue PATCH, review/liveness policy, and native finalization arbitration | `task`
`comments`
`interactions`
`blockers`
`audit`
`run` | 8 | semantic operation receipt, work assessment, issue-status decision, and terminal causality | Production semantic binding and additive typed operation/conflict receipts remain unimplemented. |
| [`cm` — Comments](#behavior-group-cm-comments) | always tools | `get_task_history`
`report_progress` | `append_audit_record` | issue comment list/get/create routes | `task`
`comments`
`actor`
`idempotency`
`audit` | 6 | bounded read result or idempotent comment-write receipt plus audit reference | Active-task binding is unbound; cross-task comment mutation is deliberately outside V1. |
-| [`se` — Search](#behavior-group-se-search) | optional discovery tools | `search_tasks`
`list_agents`
`get_agent`
`list_projects`
`list_goals` | none | company issue search and agent/project/goal list/get routes | `company`
`task`
`actor`
`project`
`goal` | 4 | bounded redacted read projections through tool-result item events | Project and goal operations are scenario-only; every real service binding is unbound. |
+| [`se` — Search](#behavior-group-se-search) | optional discovery tools | `search_tasks`
`list_agents`
`get_agent`
`list_projects`
`list_goals`
`list_project_repositories` | none | company issue search and agent/project/goal list/get routes | `company`
`task`
`actor`
`project`
`goal` | 4 | bounded redacted read projections through tool-result item events | Goal operations remain scenario-only. Project and repository discovery contracts are delivered by the live server authority; they are not implemented by the mock command dispatcher. |
| [`su` — Subtasks](#behavior-group-su-subtasks) | optional delegation tools | `create_task` | `route_wake` | company issue create, child issue, assignment, and wake services | `company`
`task`
`actor`
`blockers`
`wake`
`audit` | 4 | company/task state diff, audit reference, and continuation wake evidence | create_task is production-bound to ordinary active-issue child creation with assignment, dependency-ready wake, company checks, child limits, and durable source-scoped idempotency. |
| [`bl` — Blockers](#behavior-group-bl-blockers) | always/optional tools + control plane | `block_task`
`set_dependencies` | `schedule_blocker_wake`
`route_wake` | issue relations, blocker projection, liveness validation, and blocker wake services | `task`
`blockers`
`wake`
`actor`
`audit`
`fault` | 5 | dependency diff, block receipt, attention routing, and issue-status decision | set_dependencies is production-bound for the active issue; block_task remains unbound, and cancelled-blocker receipts still need typed additive evidence. |
| [`dp` — Documents and plans](#behavior-group-dp-documents-and-plans) | always tools; restore optional; destructive lifecycle control-plane-only | `list_documents`
`read_document`
`list_document_revisions`
`write_document` | `append_audit_record` | issue document list/read/upsert/revision/restore/lock/unlock/delete routes | `task`
`documents`
`interactions`
`idempotency`
`audit`
`fault` | 3 | bounded reads and revision-safe write/conflict/denial receipts with revision lineage | restore_document_revision is an approved optional-tool gap; lock/unlock/delete are intentionally control-plane-only. |
@@ -176,7 +179,7 @@ Behavior groups describe expected outcomes and trajectories. They do not grant t
| [`ap` — Approvals](#behavior-group-ap-approvals) | optional governance tools + governed approver | `list_approvals`
`get_approval`
`get_approval_context`
`request_approval`
`decide_approval`
`comment_on_approval` | `route_wake`
`append_audit_record` | company approval, decision, issue-link, comment, and governed-action services | `company`
`task`
`approvals`
`actor`
`wake`
`audit`
`idempotency` | 6 | governed semantic receipts, audit references, and attention/continuation linkage | Production binding and additive governed-action receipts are unbound; board-only authority stays outside grants. |
| [`ar` — Artifacts](#behavior-group-ar-artifacts) | always tools + artifact/work-product services | `register_deliverable` | `append_audit_record` | attachment upload and issue work-product routes | `task`
`artifacts`
`workProducts`
`workspace`
`audit`
`idempotency` | 4 | artifact/work-product reference and durable inspectability receipt; never binary bytes | Production upload/register composite and additive durable-reference receipt are unbound. |
| [`er` — Errors and critical rules](#behavior-group-er-errors-and-critical-rules) | runner/control plane + optional workspace/wake tools | `get_workspace_runtime`
`control_workspace_service`
`schedule_wake`
`inspect_operation_result` | `release_task`
`enforce_budget`
`persist_run`
`replay_run`
`reconcile_run` | workspace runtime, monitor/recovery, budget, run persistence/replay, release, and terminal services | `workspace`
`budget`
`run`
`wake`
`audit`
`idempotency`
`fault` | 9 | runtime/workspace/attention/run lifecycle, typed denials, replay facts, and terminal causality | Budget stop reasons and semantic denial/conflict receipts require additive v1 envelopes; inspect_operation_result remains scenario-only. |
-| [`rf` — Reference files](#behavior-group-rf-reference-files) | optional domain tools + test-only escape hatch | `list_cases`
`upsert_case`
`list_routines`
`manage_routine`
`list_company_skills`
`sync_company_skills`
`list_secret_metadata`
`read_secret_value`
`export_company`
`administer_company`
`generic_api_request`
`search_api`
`call_api` | `append_audit_record` | case, routine, company-skill, secret, portability, and administration services | `company`
`cases`
`routines`
`skills`
`secrets`
`audit`
`fault` | 22 | bounded domain projections, redacted broker receipts, company diffs, and audit references | These operations are scenario-only except generic_api_request, which is test-only; broad administer_company is deferred and cannot claim product coverage. Production escape-hatch and paired dedicated-tool regressions are recorded separately in paperclip-evals/evals/runner-api-tools; the legacy scenario count is not evidence of that coverage. |
+| [`rf` — Reference files](#behavior-group-rf-reference-files) | optional domain tools + test-only escape hatch | `list_cases`
`upsert_case`
`list_routines`
`manage_routine`
`list_company_skills`
`sync_company_skills`
`list_secret_metadata`
`read_secret_value`
`export_company`
`administer_company`
`generic_api_request`
`search_api`
`call_api`
`create_project` | `append_audit_record` | project, case, routine, company-skill, secret, portability, and administration services | `company`
`cases`
`routines`
`skills`
`secrets`
`audit`
`fault` | 22 | bounded domain projections, redacted broker receipts, company diffs, and audit references | Project creation and API tools require the live server authority; generic_api_request is test-only and the remaining domain operations are scenario-only. Broad administer_company is deferred and cannot claim product coverage. Production API and paired dedicated-tool regressions are recorded separately in paperclip-evals/evals/runner-api-tools; the legacy scenario count is not evidence of that coverage. |
| [`mh` — Multi-hop](#behavior-group-mh-multi-hop) | composed semantic operations + control-plane continuation | `create_task`
`set_dependencies`
`request_human_input`
`request_approval`
`register_deliverable` | `route_wake`
`reconcile_run` | delegation, dependency, interaction, approval, artifact, and terminal orchestration services | `task`
`blockers`
`interactions`
`approvals`
`artifacts`
`wake`
`run`
`audit` | 4 | correlated operation receipts, state diffs, attention hops, work assessment, status decision, and terminal outcome | No generic transaction tool is allowed; shared mock/real conformance must prove each composed effect. |
| [`rs` — Restraint and no-call](#behavior-group-rs-restraint-and-no-call) | policy/exposure layer | `answer_status_question`
`read_secret_value`
`generic_api_request` | `enforce_budget` | task-mode, secret-broker, test-scope, pause, and budget policy checks | `actor`
`task`
`budget`
`secrets`
`audit`
`fault` | 3 | absence of forbidden effects plus typed policy denial/redaction receipts when a call is attempted | Typed redaction/authorization receipts need additive v1 evidence; generic_api_request is never a product fallback. |
| [`wk` — Wake situations](#behavior-group-wk-wake-situations) | control plane + always context/history tools | `get_task_context`
`get_task_history`
`schedule_wake` | `select_work`
`route_wake` | wakeup requests, heartbeat context, comment/interaction/approval/blocker wake routing, and scheduled wake services | `wake`
`task`
`comments`
`interactions`
`approvals`
`blockers`
`run` | 8 | attention request routing/resolution plus resumed session/run causality | Production scheduling binding is unbound; control-plane routing remains non-callable. |
@@ -453,10 +456,10 @@ Current responsibility-based paths are normative. Numbered `phase-*` or mileston
### Catalog split and deliberate replacement
- Scenario/eval catalog: **37** operations.
-- Live dispatcher catalog: **30** operations.
-- Shared: **24**; union/canonical authority: **43**.
-- Scenario-only: `administer_company`, `export_company`, `inspect_operation_result`, `list_cases`, `list_company_skills`, `list_goals`, `list_projects`, `list_routines`, `list_secret_metadata`, `manage_routine`, `read_secret_value`, `sync_company_skills`, `upsert_case`.
-- Live-only: `call_api`, `get_agent`, `get_approval`, `get_approval_context`, `schedule_wake`, `search_api`.
+- Live dispatcher catalog: **33** operations.
+- Shared: **25**; union/canonical authority: **45**.
+- Scenario-only: `administer_company`, `export_company`, `inspect_operation_result`, `list_cases`, `list_company_skills`, `list_goals`, `list_routines`, `list_secret_metadata`, `manage_routine`, `read_secret_value`, `sync_company_skills`, `upsert_case`.
+- Live-only: `call_api`, `create_project`, `get_agent`, `get_approval`, `get_approval_context`, `list_project_repositories`, `schedule_wake`, `search_api`.
- The generated provider contract contains exactly the live catalog; the canonical union remains the migration authority until all scenario-only operations are either implemented, deferred, or removed by an explicit reconciliation decision.
- `generic_api_request` stays exported only for controlled tests and cannot be cited as real-surface, mock-parity, or PRP product coverage.
diff --git a/packages/paperclip-runner/src/eval/workflow-evals.test.ts b/packages/paperclip-runner/src/eval/workflow-evals.test.ts
index 6ad8501468..58653ac45d 100644
--- a/packages/paperclip-runner/src/eval/workflow-evals.test.ts
+++ b/packages/paperclip-runner/src/eval/workflow-evals.test.ts
@@ -466,13 +466,13 @@ describe("workflow reports and stress traceability", () => {
candidateFailures: 36,
});
expect(report.coverage).toMatchObject({
- canonicalOperations: 43,
+ canonicalOperations: 45,
capabilityCases: 106,
workflows: 12,
stressFindings: 44,
stressExclusions: 1,
});
- expect(report.coverage.operations).toHaveLength(43);
+ expect(report.coverage.operations).toHaveLength(45);
expect(report.coverage.composedWorkflows).toHaveLength(12);
expect(
report.coverage.operations.find(
@@ -480,7 +480,7 @@ describe("workflow reports and stress traceability", () => {
)?.workflowIds.length,
).toBeGreaterThan(0);
expect(renderRunnerWorkflowMarkdown(report)).toContain(
- "43 operations · 106 capability cases · 12 workflows",
+ "45 operations · 106 capability cases · 12 workflows",
);
expect(renderRunnerWorkflowJUnit(report)).toContain(
'tests="36" failures="36" skipped="0"',
diff --git a/packages/paperclip-runner/src/semantic-tools/dispatcher.ts b/packages/paperclip-runner/src/semantic-tools/dispatcher.ts
index 36af0db38d..0fbf36cfbf 100644
--- a/packages/paperclip-runner/src/semantic-tools/dispatcher.ts
+++ b/packages/paperclip-runner/src/semantic-tools/dispatcher.ts
@@ -181,7 +181,18 @@ export class CapabilitySemanticDispatcher {
return {
...createCapabilitySemanticPolicyContext(
context,
- scenario,
+ {
+ ...scenario,
+ // These descriptors belong to the server's authenticated project
+ // authority. This mock command port has no project/repository binding;
+ // it must neither advertise nor accept them merely for lacking claims.
+ denyOperations: [...new Set([
+ ...(scenario.denyOperations ?? []),
+ "create_project" as const,
+ "list_project_repositories" as const,
+ "list_projects" as const,
+ ])],
+ },
this.options.explicitClaims ?? context.capabilities,
),
runId,
diff --git a/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts b/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts
index eb9d40e7ae..bc024a1f75 100644
--- a/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts
+++ b/packages/paperclip-runner/src/semantic-tools/semantic-tools.test.ts
@@ -53,7 +53,7 @@ describe("Capability semantic catalog and authorization", () => {
it("publishes a stable narrow catalog without credentials or control-plane-owned tools", () => {
const names = CAPABILITY_SEMANTIC_TOOL_CATALOG.map((tool) => tool.operationId);
expect(new Set(names).size).toBe(names.length);
- expect(names).toHaveLength(30);
+ expect(names).toHaveLength(33);
expect(names).toContain("get_task_context");
expect(names).toContain("finish_task");
expect(names).not.toContain("checkout_task");
@@ -110,6 +110,15 @@ describe("Capability semantic catalog and authorization", () => {
const found = dispatcher.discoverTools(OPEN.identity.runId, "create child task approval secret admin");
expect(found.operations).toEqual([]);
expect(JSON.stringify(found.operations)).not.toMatch(/create_task|approval|secret|administer_company/);
+ const before = adapter.snapshot().revision;
+ for (const operationId of ["create_project", "list_project_repositories", "list_projects"] as const) {
+ expect(dispatcher.listTools(OPEN.identity.runId).map((tool) => tool.name)).not.toContain(operationId);
+ expect(await dispatcher.dispatch({
+ runId: OPEN.identity.runId, callId: `unbound-${operationId}`, operationId,
+ input: operationId === "create_project" ? { name: "Unbound", idempotencyKey: "unbound-project" } : {},
+ })).toMatchObject({ ok: false, denial: { code: "scenario_denied" } });
+ }
+ expect(adapter.snapshot().revision).toBe(before);
});
it("executes a granted optional operation through the mock port", async () => {