Add durable semantic tool receipts (#12353)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Semantic tools cross a trust boundary between a provider and the
control plane
> - Durable runs need exact input, result, denial, duplicate, and
reconciliation receipts
> - Replay must reject unsupported required versions and mismatched
receipt pairs
> - This pull request adds the receipt builders and deterministic replay
fixtures
> - It keeps newer sequence and gap safety limits from the current stack
> - The benefit is auditable semantic activity before more providers use
it

## Linked Issues or Issue Description

**Subsystem affected**

packages/paperclip-runner

**Problem or motivation**

Semantic tool calls have basic authorization records, but durable replay
does not yet cover reconciled calls, denial redaction, duplicate
receipts, governance targets, or artifact references.

**Proposed solution**

Add bounded semantic receipt builders, a reconciled phase, strict pair
binding, fail-closed version checks, and generated replay oracles for
the important lifecycle cases.

**Alternatives considered**

The runner could store provider-native tool payloads. That would weaken
protocol portability and make redaction and retry behavior
provider-specific.

**Roadmap alignment**

This supports the existing experimental Paperclip Runner rollout. It
does not enable a production adapter.

## What Changed

- Add semantic input and result receipt builders.
- Add optional reconciliation receipts for pending calls.
- Reject unsupported semantic receipt versions.
- Validate receipt correlation, operation, idempotency, and digest
bindings.
- Add deterministic replay fixtures and generated golden outputs.

## Verification

- `pnpm --filter @paperclipai/paperclip-runner test:typescript`
- `pnpm --filter @paperclipai/paperclip-runner typecheck:typescript`
- `pnpm -r typecheck`
- `pnpm build`
- Replay golden and protocol manifest checks pass.
- The branch changes 27 files relative to its declared base.

## Risks

The main risk is accepting a receipt that belongs to another call or
replaying a duplicate as a new mutation. Binding checks compare
correlation, operation, idempotency, and content digest fields. Fixtures
cover denials, duplicates, governance chains, optional fields,
artifacts, and unsupported versions.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI Codex, `gpt-5`, with agentic reasoning, tool use, and code
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked an existing public issue or described the
issue in-PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Dotta 2026-08-29 21:48:06 -05:00 committed by GitHub
parent d18e281c33
commit e18632ebcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 2176 additions and 25 deletions

View File

@ -0,0 +1,136 @@
{
"schema": "paperclip.prp.session-snapshot.v1",
"fixtureName": "Semantic tool artifact happy path",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_happy",
"issueId": "issue_semantic_happy",
"runId": "run_semantic_happy",
"environmentLeaseId": "lease_semantic_happy",
"runnerInstanceId": "runner_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"driverSessionId": "driver_semantic_happy"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": {
"kind": "provider-neutral",
"version": "1.0.0"
},
"steer": true,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [
{
"operationId": "register_deliverable",
"version": 1,
"availability": "available",
"redactionDisposition": "digest_only",
"requiredClaims": []
}
]
},
"unsupported": []
},
"runPhase": "terminal",
"sessionState": "not_started",
"turnState": "not_started",
"activeTurnId": null,
"items": [],
"requests": [],
"proposedResult": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "done",
"summary": "The artifact was registered through the semantic tool receipt.",
"completionClaim": {
"contractRevision": "semantic-receipts-v1",
"objectiveSatisfied": true,
"criteria": [],
"remainingWork": []
},
"evidence": [
{
"receiptId": "receipt_semantic_happy"
}
],
"verification": [
{
"commandOrCheck": "semantic artifact receipt",
"status": "passed",
"artifactRef": "artifact_semantic_happy"
}
],
"attentionRequests": [],
"artifacts": [
{
"kind": "artifact",
"ref": "artifact_semantic_happy",
"title": "Semantic receipt report"
}
]
},
"terminal": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "done",
"workAssessmentId": "assessment_semantic_happy",
"statusDecisionId": "decision_semantic_happy"
},
"timeline": [
{
"position": 1,
"sourceEventId": "semantic_happy_01",
"sourceSeq": 1,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:00:00.000Z",
"itemId": "item_semantic_happy",
"summary": "mcp_app tool_input"
},
{
"position": 2,
"sourceEventId": "semantic_happy_02",
"sourceSeq": 2,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:00:00.010Z",
"itemId": "item_semantic_happy",
"summary": "mcp_app tool_result"
},
{
"position": 3,
"sourceEventId": "semantic_happy_03",
"sourceSeq": 3,
"eventType": "run.result.proposed",
"emittedAt": "2026-08-11T10:00:00.020Z",
"summary": "The artifact was registered through the semantic tool receipt."
},
{
"position": 4,
"sourceEventId": "semantic_happy_04",
"sourceSeq": 4,
"eventType": "run.terminal",
"emittedAt": "2026-08-11T10:00:00.030Z",
"summary": "Run succeeded"
}
],
"sourceCursors": {
"runner:runner_semantic_happy": 4
},
"processedEventIds": [
"semantic_happy_01",
"semantic_happy_02",
"semantic_happy_03",
"semantic_happy_04"
],
"duplicateEventIds": [],
"outOfOrderEventIds": [],
"gaps": [],
"integrity": "complete"
}

View File

@ -0,0 +1,9 @@
{
"runId": "run_semantic_happy",
"integrity": "complete",
"timelineCount": 4,
"duplicateEventIds": [],
"gaps": [],
"turnTerminalState": "completed",
"runTerminalState": "succeeded"
}

View File

@ -0,0 +1,172 @@
{
"schema": "paperclip.prp.session-snapshot.v1",
"fixtureName": "Semantic tool conflict and duplicate retry",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_retry",
"issueId": "issue_semantic_retry",
"runId": "run_semantic_retry",
"environmentLeaseId": "lease_semantic_retry",
"runnerInstanceId": "runner_semantic_retry",
"normalizedSessionId": "session_semantic_retry",
"driverSessionId": "driver_semantic_retry"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": {
"kind": "provider-neutral",
"version": "1.0.0"
},
"steer": false,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [
{
"operationId": "write_document",
"version": 1,
"availability": "available",
"redactionDisposition": "digest_only",
"requiredClaims": []
}
]
},
"unsupported": []
},
"runPhase": "terminal",
"sessionState": "not_started",
"turnState": "not_started",
"activeTurnId": null,
"items": [],
"requests": [],
"proposedResult": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "done",
"summary": "The revision-safe retry committed once.",
"completionClaim": {
"contractRevision": "semantic-receipts-v1",
"objectiveSatisfied": true,
"criteria": [],
"remainingWork": []
},
"evidence": [
{
"receiptId": "receipt_semantic_conflict"
},
{
"receiptId": "receipt_semantic_document_commit"
}
],
"verification": [
{
"commandOrCheck": "conflict then exact duplicate retry",
"status": "passed"
}
],
"attentionRequests": [],
"artifacts": []
},
"terminal": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "done",
"workAssessmentId": "assessment_semantic_retry",
"statusDecisionId": "decision_semantic_retry"
},
"timeline": [
{
"position": 1,
"sourceEventId": "semantic_retry_01",
"sourceSeq": 1,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:20:00.000Z",
"itemId": "item_semantic_conflict",
"summary": "mcp_app tool_input"
},
{
"position": 2,
"sourceEventId": "semantic_retry_02",
"sourceSeq": 2,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:20:00.010Z",
"itemId": "item_semantic_conflict",
"summary": "mcp_app tool_result"
},
{
"position": 3,
"sourceEventId": "semantic_retry_03",
"sourceSeq": 3,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:20:00.020Z",
"itemId": "item_semantic_fresh",
"summary": "mcp_app tool_input"
},
{
"position": 4,
"sourceEventId": "semantic_retry_04",
"sourceSeq": 4,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:20:00.030Z",
"itemId": "item_semantic_fresh",
"summary": "mcp_app tool_result"
},
{
"position": 5,
"sourceEventId": "semantic_retry_05",
"sourceSeq": 5,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:20:00.040Z",
"itemId": "item_semantic_duplicate",
"summary": "mcp_app tool_input"
},
{
"position": 6,
"sourceEventId": "semantic_retry_06",
"sourceSeq": 6,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:20:00.050Z",
"itemId": "item_semantic_duplicate",
"summary": "mcp_app tool_result"
},
{
"position": 7,
"sourceEventId": "semantic_retry_07",
"sourceSeq": 7,
"eventType": "run.result.proposed",
"emittedAt": "2026-08-11T10:20:00.060Z",
"summary": "The revision-safe retry committed once."
},
{
"position": 8,
"sourceEventId": "semantic_retry_08",
"sourceSeq": 8,
"eventType": "run.terminal",
"emittedAt": "2026-08-11T10:20:00.070Z",
"summary": "Run succeeded"
}
],
"sourceCursors": {
"runner:runner_semantic_retry": 8
},
"processedEventIds": [
"semantic_retry_01",
"semantic_retry_02",
"semantic_retry_03",
"semantic_retry_04",
"semantic_retry_05",
"semantic_retry_06",
"semantic_retry_07",
"semantic_retry_08"
],
"duplicateEventIds": [],
"outOfOrderEventIds": [],
"gaps": [],
"integrity": "complete"
}

View File

@ -0,0 +1,9 @@
{
"runId": "run_semantic_retry",
"integrity": "complete",
"timelineCount": 8,
"duplicateEventIds": [],
"gaps": [],
"turnTerminalState": "completed",
"runTerminalState": "succeeded"
}

View File

@ -0,0 +1,137 @@
{
"schema": "paperclip.prp.session-snapshot.v1",
"fixtureName": "Semantic tool denial and redaction",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_denied",
"issueId": "issue_semantic_denied",
"runId": "run_semantic_denied",
"environmentLeaseId": "lease_semantic_denied",
"runnerInstanceId": "runner_semantic_denied",
"normalizedSessionId": "session_semantic_denied",
"driverSessionId": "driver_semantic_denied"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": {
"kind": "provider-neutral",
"version": "1.0.0"
},
"steer": false,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [
{
"operationId": "decide_approval",
"version": 1,
"availability": "denied",
"redactionDisposition": "digest_only",
"requiredClaims": [
"governance:approvals:decide"
],
"reasonCode": "required_claim_missing"
}
]
},
"unsupported": []
},
"runPhase": "terminal",
"sessionState": "not_started",
"turnState": "not_started",
"activeTurnId": null,
"items": [],
"requests": [],
"proposedResult": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "needs_review",
"summary": "The governed action was denied without fallback.",
"completionClaim": {
"contractRevision": "semantic-receipts-v1",
"objectiveSatisfied": false,
"criteria": [],
"remainingWork": [
{
"description": "An authorized approver must decide.",
"blocksCompletion": true
}
]
},
"evidence": [
{
"receiptId": "receipt_semantic_denied"
}
],
"verification": [
{
"commandOrCheck": "no fallback semantic call",
"status": "passed"
}
],
"attentionRequests": [],
"artifacts": []
},
"terminal": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "needs_review",
"workAssessmentId": "assessment_semantic_denied",
"statusDecisionId": "decision_semantic_denied"
},
"timeline": [
{
"position": 1,
"sourceEventId": "semantic_denied_01",
"sourceSeq": 1,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:10:00.000Z",
"itemId": "item_semantic_denied",
"summary": "mcp_app tool_input"
},
{
"position": 2,
"sourceEventId": "semantic_denied_02",
"sourceSeq": 2,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:10:00.010Z",
"itemId": "item_semantic_denied",
"summary": "mcp_app tool_result"
},
{
"position": 3,
"sourceEventId": "semantic_denied_03",
"sourceSeq": 3,
"eventType": "run.result.proposed",
"emittedAt": "2026-08-11T10:10:00.020Z",
"summary": "The governed action was denied without fallback."
},
{
"position": 4,
"sourceEventId": "semantic_denied_04",
"sourceSeq": 4,
"eventType": "run.terminal",
"emittedAt": "2026-08-11T10:10:00.030Z",
"summary": "Run succeeded"
}
],
"sourceCursors": {
"runner:runner_semantic_denied": 4
},
"processedEventIds": [
"semantic_denied_01",
"semantic_denied_02",
"semantic_denied_03",
"semantic_denied_04"
],
"duplicateEventIds": [],
"outOfOrderEventIds": [],
"gaps": [],
"integrity": "complete"
}

View File

@ -0,0 +1,9 @@
{
"runId": "run_semantic_denied",
"integrity": "complete",
"timelineCount": 4,
"duplicateEventIds": [],
"gaps": [],
"turnTerminalState": "completed",
"runTerminalState": "succeeded"
}

View File

@ -0,0 +1,139 @@
{
"schema": "paperclip.prp.session-snapshot.v1",
"fixtureName": "Semantic governance target and continuation chain",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_governance",
"issueId": "issue_semantic_governance",
"runId": "run_semantic_governance",
"environmentLeaseId": "lease_semantic_governance",
"runnerInstanceId": "runner_semantic_governance",
"normalizedSessionId": "session_semantic_governance",
"driverSessionId": "driver_semantic_governance"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "reuse_per_issue",
"driver": {
"kind": "provider-neutral",
"version": "1.0.0"
},
"steer": true,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [
{
"operationId": "request_human_input",
"version": 1,
"availability": "available",
"redactionDisposition": "digest_only",
"requiredClaims": []
}
]
},
"unsupported": []
},
"runPhase": "terminal",
"sessionState": "not_started",
"turnState": "not_started",
"activeTurnId": null,
"items": [],
"requests": [],
"proposedResult": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "yielded",
"summary": "The immutable decision target is waiting on a bounded monitor continuation.",
"completionClaim": {
"contractRevision": "semantic-receipts-v1",
"objectiveSatisfied": false,
"criteria": [],
"remainingWork": [
{
"description": "Wait for the interaction response.",
"blocksCompletion": true
}
]
},
"evidence": [
{
"receiptId": "receipt_semantic_governance"
}
],
"verification": [
{
"commandOrCheck": "immutable target and causal chain",
"status": "passed"
}
],
"attentionRequests": [],
"artifacts": [],
"continuation": {
"kind": "monitor",
"summary": "Recheck after the typed response wake.",
"idempotencyKey": "monitor_semantic_governance"
}
},
"terminal": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "yielded",
"workAssessmentId": "assessment_semantic_governance",
"statusDecisionId": "decision_semantic_governance"
},
"timeline": [
{
"position": 1,
"sourceEventId": "semantic_governance_01",
"sourceSeq": 1,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:30:00.000Z",
"itemId": "item_semantic_governance",
"summary": "mcp_app tool_input"
},
{
"position": 2,
"sourceEventId": "semantic_governance_02",
"sourceSeq": 2,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:30:00.010Z",
"itemId": "item_semantic_governance",
"summary": "mcp_app tool_result"
},
{
"position": 3,
"sourceEventId": "semantic_governance_03",
"sourceSeq": 3,
"eventType": "run.result.proposed",
"emittedAt": "2026-08-11T10:30:00.020Z",
"summary": "The immutable decision target is waiting on a bounded monitor continuation."
},
{
"position": 4,
"sourceEventId": "semantic_governance_04",
"sourceSeq": 4,
"eventType": "run.terminal",
"emittedAt": "2026-08-11T10:30:00.030Z",
"summary": "Run succeeded"
}
],
"sourceCursors": {
"runner:runner_semantic_governance": 4
},
"processedEventIds": [
"semantic_governance_01",
"semantic_governance_02",
"semantic_governance_03",
"semantic_governance_04"
],
"duplicateEventIds": [],
"outOfOrderEventIds": [],
"gaps": [],
"integrity": "complete"
}

View File

@ -0,0 +1,9 @@
{
"runId": "run_semantic_governance",
"integrity": "complete",
"timelineCount": 4,
"duplicateEventIds": [],
"gaps": [],
"turnTerminalState": "completed",
"runTerminalState": "succeeded"
}

View File

@ -0,0 +1,133 @@
{
"schema": "paperclip.prp.session-snapshot.v1",
"fixtureName": "Semantic tool unknown optional envelope fields",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_future",
"issueId": "issue_semantic_future",
"runId": "run_semantic_future",
"environmentLeaseId": "lease_semantic_future",
"runnerInstanceId": "runner_semantic_future",
"normalizedSessionId": "session_semantic_future",
"driverSessionId": "driver_semantic_future"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": {
"kind": "provider-neutral",
"version": "1.0.0"
},
"steer": false,
"interrupt": true,
"resume": true,
"runtimeRequests": false,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [
{
"operationId": "get_task_context",
"version": 1,
"availability": "available",
"redactionDisposition": "digest_only",
"requiredClaims": [],
"futureAvailabilityHint": "optional"
}
],
"futureNegotiationHint": {
"safe": true
}
},
"unsupported": []
},
"runPhase": "terminal",
"sessionState": "not_started",
"turnState": "not_started",
"activeTurnId": null,
"items": [],
"requests": [],
"proposedResult": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "done",
"summary": "Unknown optional receipt fields were ignored by the v1 projection.",
"completionClaim": {
"contractRevision": "semantic-receipts-v1",
"objectiveSatisfied": true,
"criteria": [],
"remainingWork": []
},
"evidence": [
{
"receiptId": "receipt_semantic_future"
}
],
"verification": [
{
"commandOrCheck": "optional field projection stability",
"status": "passed"
}
],
"attentionRequests": [],
"artifacts": []
},
"terminal": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "done",
"workAssessmentId": "assessment_semantic_future",
"statusDecisionId": "decision_semantic_future"
},
"timeline": [
{
"position": 1,
"sourceEventId": "semantic_future_01",
"sourceSeq": 1,
"eventType": "mcp_app.tool_input",
"emittedAt": "2026-08-11T10:50:00.000Z",
"itemId": "item_semantic_future",
"summary": "mcp_app tool_input"
},
{
"position": 2,
"sourceEventId": "semantic_future_02",
"sourceSeq": 2,
"eventType": "mcp_app.tool_result",
"emittedAt": "2026-08-11T10:50:00.010Z",
"itemId": "item_semantic_future",
"summary": "mcp_app tool_result"
},
{
"position": 3,
"sourceEventId": "semantic_future_03",
"sourceSeq": 3,
"eventType": "run.result.proposed",
"emittedAt": "2026-08-11T10:50:00.020Z",
"summary": "Unknown optional receipt fields were ignored by the v1 projection."
},
{
"position": 4,
"sourceEventId": "semantic_future_04",
"sourceSeq": 4,
"eventType": "run.terminal",
"emittedAt": "2026-08-11T10:50:00.030Z",
"summary": "Run succeeded"
}
],
"sourceCursors": {
"runner:runner_semantic_future": 4
},
"processedEventIds": [
"semantic_future_01",
"semantic_future_02",
"semantic_future_03",
"semantic_future_04"
],
"duplicateEventIds": [],
"outOfOrderEventIds": [],
"gaps": [],
"integrity": "complete"
}

View File

@ -0,0 +1,9 @@
{
"runId": "run_semantic_future",
"integrity": "complete",
"timelineCount": 4,
"duplicateEventIds": [],
"gaps": [],
"turnTerminalState": "completed",
"runTerminalState": "succeeded"
}

View File

@ -0,0 +1,186 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Semantic tool artifact happy path",
"description": "A provider-neutral register_deliverable call emits safe input and result receipts linked to the terminal artifact result.",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_happy",
"issueId": "issue_semantic_happy",
"runId": "run_semantic_happy",
"environmentLeaseId": "lease_semantic_happy",
"runnerInstanceId": "runner_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"driverSessionId": "driver_semantic_happy"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": true,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [{
"operationId": "register_deliverable",
"version": 1,
"availability": "available",
"redactionDisposition": "digest_only",
"requiredClaims": []
}]
},
"unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1",
"sourceEventId": "semantic_happy_01",
"sourceSeq": 1,
"sourceInstanceId": "runner_semantic_happy",
"sourceKind": "runner",
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"itemId": "item_semantic_happy",
"eventType": "mcp_app.tool_input",
"schemaVersion": 1,
"priority": 1,
"emittedAt": "2026-08-11T10:00:00.000Z",
"payload": {
"semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1",
"schemaVersion": 1,
"phase": "input",
"operationId": "register_deliverable",
"callId": "call_semantic_happy",
"correlation": {
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"itemId": "item_semantic_happy"
},
"idempotencyKey": "artifact_once",
"content": {
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"redactionDisposition": "digest_only",
"references": [{ "kind": "task", "id": "issue_semantic_happy" }]
}
}
}
},
{
"schema": "paperclip.prp.event.v1",
"sourceEventId": "semantic_happy_02",
"sourceSeq": 2,
"sourceInstanceId": "runner_semantic_happy",
"sourceKind": "runner",
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"itemId": "item_semantic_happy",
"eventType": "mcp_app.tool_result",
"schemaVersion": 1,
"priority": 1,
"emittedAt": "2026-08-11T10:00:00.010Z",
"payload": {
"semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1",
"schemaVersion": 1,
"phase": "result",
"operationId": "register_deliverable",
"callId": "call_semantic_happy",
"correlation": {
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"itemId": "item_semantic_happy"
},
"idempotencyKey": "artifact_once",
"content": {
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"redactionDisposition": "allowlisted_references",
"references": [
{ "kind": "artifact", "id": "artifact_semantic_happy" },
{ "kind": "work_product", "id": "work_product_semantic_happy" }
]
},
"outcome": "succeeded",
"code": "ok",
"retryable": false,
"authorizationBoundary": "active_task",
"operationReceiptId": "receipt_semantic_happy",
"auditReceiptId": "audit_semantic_happy",
"currentRevision": 2,
"artifactRefs": [
{ "kind": "artifact", "id": "artifact_semantic_happy" },
{ "kind": "work_product", "id": "work_product_semantic_happy" }
]
}
}
},
{
"schema": "paperclip.prp.event.v1",
"sourceEventId": "semantic_happy_03",
"sourceSeq": 3,
"sourceInstanceId": "runner_semantic_happy",
"sourceKind": "runner",
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"eventType": "run.result.proposed",
"schemaVersion": 1,
"priority": 0,
"emittedAt": "2026-08-11T10:00:00.020Z",
"payload": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "done",
"summary": "The artifact was registered through the semantic tool receipt.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_happy" }],
"verification": [{ "commandOrCheck": "semantic artifact receipt", "status": "passed", "artifactRef": "artifact_semantic_happy" }],
"attentionRequests": [],
"artifacts": [{ "kind": "artifact", "ref": "artifact_semantic_happy", "title": "Semantic receipt report" }]
}
},
{
"schema": "paperclip.prp.event.v1",
"sourceEventId": "semantic_happy_04",
"sourceSeq": 4,
"sourceInstanceId": "runner_semantic_happy",
"sourceKind": "runner",
"runId": "run_semantic_happy",
"normalizedSessionId": "session_semantic_happy",
"turnId": "turn_semantic_happy",
"eventType": "run.terminal",
"schemaVersion": 1,
"priority": 0,
"emittedAt": "2026-08-11T10:00:00.030Z",
"payload": {
"schema": "paperclip.prp.terminal.v1",
"turnTerminalState": "completed",
"runTerminalState": "succeeded",
"reportedWorkDisposition": "done",
"workAssessmentId": "assessment_semantic_happy",
"statusDecisionId": "decision_semantic_happy"
}
}
],
"requests": [],
"result": {
"schema": "paperclip.run_result.v1",
"reportedWorkDisposition": "done",
"summary": "The artifact was registered through the semantic tool receipt.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_happy" }],
"verification": [{ "commandOrCheck": "semantic artifact receipt", "status": "passed", "artifactRef": "artifact_semantic_happy" }],
"attentionRequests": [],
"artifacts": [{ "kind": "artifact", "ref": "artifact_semantic_happy", "title": "Semantic receipt report" }]
}
}

View File

@ -0,0 +1,120 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Semantic tool conflict and duplicate retry",
"description": "A stale document write conflicts, a fresh retry succeeds, and an exact idempotent retry returns the original operation receipt.",
"identity": {
"schema": "paperclip.prp.identity.v1", "companyId": "company_semantic_retry", "issueId": "issue_semantic_retry",
"runId": "run_semantic_retry", "environmentLeaseId": "lease_semantic_retry", "runnerInstanceId": "runner_semantic_retry",
"normalizedSessionId": "session_semantic_retry", "driverSessionId": "driver_semantic_retry"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1", "sessionReusePolicy": "new_per_run",
"driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": false, "interrupt": true, "resume": true, "runtimeRequests": true, "structuredResult": true, "typedEvents": true,
"semanticTools": { "schema": "paperclip.prp.semantic_tools.v1", "schemaVersion": 1, "operations": [{
"operationId": "write_document", "version": 1, "availability": "available", "redactionDisposition": "digest_only", "requiredClaims": []
}] },
"unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_01", "sourceSeq": 1, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_conflict", "eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.000Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input", "operationId": "write_document", "callId": "call_semantic_conflict",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_conflict" },
"idempotencyKey": "write_plan_stale",
"content": { "digest": "sha256:1111111111111111111111111111111111111111111111111111111111111111", "redactionDisposition": "digest_only", "references": [{ "kind": "document_revision", "id": "revision_plan_stale" }] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_02", "sourceSeq": 2, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_conflict", "eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.010Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "write_document", "callId": "call_semantic_conflict",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_conflict" },
"idempotencyKey": "write_plan_stale",
"content": { "digest": "sha256:2222222222222222222222222222222222222222222222222222222222222222", "redactionDisposition": "allowlisted_references", "references": [{ "kind": "document_revision", "id": "revision_plan_2" }] },
"outcome": "conflict", "code": "document_revision_conflict", "retryable": true, "authorizationBoundary": "revision",
"operationReceiptId": "receipt_semantic_conflict", "auditReceiptId": "audit_semantic_conflict", "currentRevision": "revision_plan_2"
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_03", "sourceSeq": 3, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_fresh", "eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.020Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input", "operationId": "write_document", "callId": "call_semantic_fresh",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_fresh" },
"idempotencyKey": "write_plan_retry",
"content": { "digest": "sha256:3333333333333333333333333333333333333333333333333333333333333333", "redactionDisposition": "digest_only", "references": [{ "kind": "document_revision", "id": "revision_plan_2" }] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_04", "sourceSeq": 4, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_fresh", "eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.030Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "write_document", "callId": "call_semantic_fresh",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_fresh" },
"idempotencyKey": "write_plan_retry",
"content": { "digest": "sha256:4444444444444444444444444444444444444444444444444444444444444444", "redactionDisposition": "allowlisted_references", "references": [{ "kind": "document_revision", "id": "revision_plan_3" }] },
"outcome": "succeeded", "code": "ok", "retryable": false, "authorizationBoundary": "revision",
"operationReceiptId": "receipt_semantic_document_commit", "auditReceiptId": "audit_semantic_document_commit", "currentRevision": "revision_plan_3"
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_05", "sourceSeq": 5, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_duplicate", "eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.040Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input", "operationId": "write_document", "callId": "call_semantic_duplicate",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_duplicate" },
"idempotencyKey": "write_plan_retry",
"content": { "digest": "sha256:3333333333333333333333333333333333333333333333333333333333333333", "redactionDisposition": "digest_only", "references": [{ "kind": "document_revision", "id": "revision_plan_2" }] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_06", "sourceSeq": 6, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"itemId": "item_semantic_duplicate", "eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:20:00.050Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "write_document", "callId": "call_semantic_duplicate",
"correlation": { "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry", "itemId": "item_semantic_duplicate" },
"idempotencyKey": "write_plan_retry",
"content": { "digest": "sha256:4444444444444444444444444444444444444444444444444444444444444444", "redactionDisposition": "allowlisted_references", "references": [{ "kind": "document_revision", "id": "revision_plan_3" }] },
"outcome": "duplicate", "code": "duplicate", "retryable": false, "authorizationBoundary": "revision",
"operationReceiptId": "receipt_semantic_document_commit", "duplicateOfReceiptId": "receipt_semantic_document_commit", "currentRevision": "revision_plan_3"
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_07", "sourceSeq": 7, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"eventType": "run.result.proposed", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:20:00.060Z",
"payload": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "The revision-safe retry committed once.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_conflict" }, { "receiptId": "receipt_semantic_document_commit" }],
"verification": [{ "commandOrCheck": "conflict then exact duplicate retry", "status": "passed" }], "attentionRequests": [], "artifacts": []
}
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_retry_08", "sourceSeq": 8, "sourceInstanceId": "runner_semantic_retry",
"sourceKind": "runner", "runId": "run_semantic_retry", "normalizedSessionId": "session_semantic_retry", "turnId": "turn_semantic_retry",
"eventType": "run.terminal", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:20:00.070Z",
"payload": { "schema": "paperclip.prp.terminal.v1", "turnTerminalState": "completed", "runTerminalState": "succeeded", "reportedWorkDisposition": "done", "workAssessmentId": "assessment_semantic_retry", "statusDecisionId": "decision_semantic_retry" }
}
],
"requests": [],
"result": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "The revision-safe retry committed once.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_conflict" }, { "receiptId": "receipt_semantic_document_commit" }],
"verification": [{ "commandOrCheck": "conflict then exact duplicate retry", "status": "passed" }], "attentionRequests": [], "artifacts": []
}
}

View File

@ -0,0 +1,102 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Semantic tool denial and redaction",
"description": "A governed action is denied with digest-only content, a typed authorization boundary, and no fallback semantic call.",
"identity": {
"schema": "paperclip.prp.identity.v1",
"companyId": "company_semantic_denied",
"issueId": "issue_semantic_denied",
"runId": "run_semantic_denied",
"environmentLeaseId": "lease_semantic_denied",
"runnerInstanceId": "runner_semantic_denied",
"normalizedSessionId": "session_semantic_denied",
"driverSessionId": "driver_semantic_denied"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1",
"sessionReusePolicy": "new_per_run",
"driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": false,
"interrupt": true,
"resume": true,
"runtimeRequests": true,
"structuredResult": true,
"typedEvents": true,
"semanticTools": {
"schema": "paperclip.prp.semantic_tools.v1",
"schemaVersion": 1,
"operations": [{
"operationId": "decide_approval",
"version": 1,
"availability": "denied",
"redactionDisposition": "digest_only",
"requiredClaims": ["governance:approvals:decide"],
"reasonCode": "required_claim_missing"
}]
},
"unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_denied_01", "sourceSeq": 1,
"sourceInstanceId": "runner_semantic_denied", "sourceKind": "runner", "runId": "run_semantic_denied",
"normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied", "itemId": "item_semantic_denied",
"eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:10:00.000Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input",
"operationId": "decide_approval", "callId": "call_semantic_denied",
"correlation": { "runId": "run_semantic_denied", "normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied", "itemId": "item_semantic_denied" },
"idempotencyKey": "denied_once",
"content": { "digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", "redactionDisposition": "redacted", "references": [] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_denied_02", "sourceSeq": 2,
"sourceInstanceId": "runner_semantic_denied", "sourceKind": "runner", "runId": "run_semantic_denied",
"normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied", "itemId": "item_semantic_denied",
"eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:10:00.010Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result",
"operationId": "decide_approval", "callId": "call_semantic_denied",
"correlation": { "runId": "run_semantic_denied", "normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied", "itemId": "item_semantic_denied" },
"idempotencyKey": "denied_once",
"content": { "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", "redactionDisposition": "redacted", "references": [] },
"outcome": "denied", "code": "required_claim_missing", "retryable": false,
"authorizationBoundary": "grant", "operationReceiptId": "receipt_semantic_denied", "auditReceiptId": "audit_semantic_denied", "currentRevision": 4
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_denied_03", "sourceSeq": 3,
"sourceInstanceId": "runner_semantic_denied", "sourceKind": "runner", "runId": "run_semantic_denied",
"normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied",
"eventType": "run.result.proposed", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:10:00.020Z",
"payload": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "needs_review",
"summary": "The governed action was denied without fallback.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": false, "criteria": [], "remainingWork": [{ "description": "An authorized approver must decide.", "blocksCompletion": true }] },
"evidence": [{ "receiptId": "receipt_semantic_denied" }],
"verification": [{ "commandOrCheck": "no fallback semantic call", "status": "passed" }],
"attentionRequests": [], "artifacts": []
}
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_denied_04", "sourceSeq": 4,
"sourceInstanceId": "runner_semantic_denied", "sourceKind": "runner", "runId": "run_semantic_denied",
"normalizedSessionId": "session_semantic_denied", "turnId": "turn_semantic_denied",
"eventType": "run.terminal", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:10:00.030Z",
"payload": { "schema": "paperclip.prp.terminal.v1", "turnTerminalState": "completed", "runTerminalState": "succeeded", "reportedWorkDisposition": "needs_review", "workAssessmentId": "assessment_semantic_denied", "statusDecisionId": "decision_semantic_denied" }
}
],
"requests": [],
"result": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "needs_review",
"summary": "The governed action was denied without fallback.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": false, "criteria": [], "remainingWork": [{ "description": "An authorized approver must decide.", "blocksCompletion": true }] },
"evidence": [{ "receiptId": "receipt_semantic_denied" }],
"verification": [{ "commandOrCheck": "no fallback semantic call", "status": "passed" }],
"attentionRequests": [], "artifacts": []
}
}

View File

@ -0,0 +1,90 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Semantic governance target and continuation chain",
"description": "A semantic result binds immutable interaction and approval targets while carrying only safe wake and monitor causal references.",
"identity": {
"schema": "paperclip.prp.identity.v1", "companyId": "company_semantic_governance", "issueId": "issue_semantic_governance",
"runId": "run_semantic_governance", "environmentLeaseId": "lease_semantic_governance", "runnerInstanceId": "runner_semantic_governance",
"normalizedSessionId": "session_semantic_governance", "driverSessionId": "driver_semantic_governance"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1", "sessionReusePolicy": "reuse_per_issue",
"driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": true, "interrupt": true, "resume": true, "runtimeRequests": true, "structuredResult": true, "typedEvents": true,
"semanticTools": { "schema": "paperclip.prp.semantic_tools.v1", "schemaVersion": 1, "operations": [{
"operationId": "request_human_input", "version": 1, "availability": "available", "redactionDisposition": "digest_only", "requiredClaims": []
}] },
"unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_governance_01", "sourceSeq": 1, "sourceInstanceId": "runner_semantic_governance",
"sourceKind": "runner", "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance",
"turnId": "turn_semantic_governance", "itemId": "item_semantic_governance", "eventType": "mcp_app.tool_input",
"schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:30:00.000Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input", "operationId": "request_human_input", "callId": "call_semantic_governance",
"correlation": { "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance", "turnId": "turn_semantic_governance", "itemId": "item_semantic_governance", "requestId": "request_semantic_governance" },
"idempotencyKey": "interaction_once",
"content": { "digest": "sha256:5555555555555555555555555555555555555555555555555555555555555555", "redactionDisposition": "digest_only", "references": [{ "kind": "document_revision", "id": "revision_plan_7" }] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_governance_02", "sourceSeq": 2, "sourceInstanceId": "runner_semantic_governance",
"sourceKind": "runner", "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance",
"turnId": "turn_semantic_governance", "itemId": "item_semantic_governance", "eventType": "mcp_app.tool_result",
"schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:30:00.010Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "request_human_input", "callId": "call_semantic_governance",
"correlation": { "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance", "turnId": "turn_semantic_governance", "itemId": "item_semantic_governance", "requestId": "request_semantic_governance" },
"idempotencyKey": "interaction_once",
"content": { "digest": "sha256:6666666666666666666666666666666666666666666666666666666666666666", "redactionDisposition": "allowlisted_references", "references": [{ "kind": "interaction", "id": "interaction_semantic_governance" }, { "kind": "wake", "id": "wake_semantic_governance" }, { "kind": "monitor", "id": "monitor_semantic_governance" }] },
"outcome": "succeeded", "code": "ok", "retryable": false, "authorizationBoundary": "governed_action",
"operationReceiptId": "receipt_semantic_governance", "auditReceiptId": "audit_semantic_governance", "currentRevision": 8,
"targets": [
{ "kind": "interaction", "id": "interaction_semantic_governance", "immutable": true, "revisionId": "revision_plan_7" },
{ "kind": "approval", "id": "approval_semantic_governance", "immutable": true, "decisionId": "decision_approval_semantic_governance" }
],
"causalRefs": [
{ "kind": "document_revision", "id": "revision_plan_7" },
{ "kind": "interaction", "id": "interaction_semantic_governance" },
{ "kind": "approval", "id": "approval_semantic_governance" },
{ "kind": "decision", "id": "decision_approval_semantic_governance" },
{ "kind": "wake", "id": "wake_semantic_governance" },
{ "kind": "monitor", "id": "monitor_semantic_governance" }
]
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_governance_03", "sourceSeq": 3, "sourceInstanceId": "runner_semantic_governance",
"sourceKind": "runner", "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance", "turnId": "turn_semantic_governance",
"eventType": "run.result.proposed", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:30:00.020Z",
"payload": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "yielded", "summary": "The immutable decision target is waiting on a bounded monitor continuation.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": false, "criteria": [], "remainingWork": [{ "description": "Wait for the interaction response.", "blocksCompletion": true }] },
"evidence": [{ "receiptId": "receipt_semantic_governance" }],
"verification": [{ "commandOrCheck": "immutable target and causal chain", "status": "passed" }],
"attentionRequests": [], "artifacts": [],
"continuation": { "kind": "monitor", "summary": "Recheck after the typed response wake.", "idempotencyKey": "monitor_semantic_governance" }
}
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_governance_04", "sourceSeq": 4, "sourceInstanceId": "runner_semantic_governance",
"sourceKind": "runner", "runId": "run_semantic_governance", "normalizedSessionId": "session_semantic_governance", "turnId": "turn_semantic_governance",
"eventType": "run.terminal", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:30:00.030Z",
"payload": { "schema": "paperclip.prp.terminal.v1", "turnTerminalState": "completed", "runTerminalState": "succeeded", "reportedWorkDisposition": "yielded", "workAssessmentId": "assessment_semantic_governance", "statusDecisionId": "decision_semantic_governance" }
}
],
"requests": [],
"result": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "yielded", "summary": "The immutable decision target is waiting on a bounded monitor continuation.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": false, "criteria": [], "remainingWork": [{ "description": "Wait for the interaction response.", "blocksCompletion": true }] },
"evidence": [{ "receiptId": "receipt_semantic_governance" }],
"verification": [{ "commandOrCheck": "immutable target and causal chain", "status": "passed" }],
"attentionRequests": [], "artifacts": [],
"continuation": { "kind": "monitor", "summary": "Recheck after the typed response wake.", "idempotencyKey": "monitor_semantic_governance" }
}
}

View File

@ -0,0 +1,72 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Semantic tool unknown optional envelope fields",
"description": "Unknown additive fields inside typed semantic envelopes validate without changing the existing v1 reducer projection.",
"identity": {
"schema": "paperclip.prp.identity.v1", "companyId": "company_semantic_future", "issueId": "issue_semantic_future", "runId": "run_semantic_future",
"environmentLeaseId": "lease_semantic_future", "runnerInstanceId": "runner_semantic_future", "normalizedSessionId": "session_semantic_future", "driverSessionId": "driver_semantic_future"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1", "sessionReusePolicy": "new_per_run", "driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": false, "interrupt": true, "resume": true, "runtimeRequests": false, "structuredResult": true, "typedEvents": true,
"semanticTools": { "schema": "paperclip.prp.semantic_tools.v1", "schemaVersion": 1, "operations": [{
"operationId": "get_task_context", "version": 1, "availability": "available", "redactionDisposition": "digest_only", "requiredClaims": [],
"futureAvailabilityHint": "optional"
}], "futureNegotiationHint": { "safe": true } },
"unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_future_01", "sourceSeq": 1, "sourceInstanceId": "runner_semantic_future",
"sourceKind": "runner", "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future", "itemId": "item_semantic_future",
"eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:50:00.000Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "input", "operationId": "get_task_context", "callId": "call_semantic_future",
"correlation": { "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future", "itemId": "item_semantic_future", "futureCorrelationHint": 1 },
"idempotencyKey": null,
"content": { "digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777", "redactionDisposition": "digest_only", "references": [], "futureContentHint": "optional" },
"futureEnvelopeHint": { "version": "1.1-preview" }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_future_02", "sourceSeq": 2, "sourceInstanceId": "runner_semantic_future",
"sourceKind": "runner", "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future", "itemId": "item_semantic_future",
"eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T10:50:00.010Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "get_task_context", "callId": "call_semantic_future",
"correlation": { "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future", "itemId": "item_semantic_future", "futureCorrelationHint": 1 },
"idempotencyKey": null,
"content": { "digest": "sha256:8888888888888888888888888888888888888888888888888888888888888888", "redactionDisposition": "digest_only", "references": [{ "kind": "task", "id": "issue_semantic_future" }], "futureContentHint": "optional" },
"outcome": "succeeded", "code": "ok", "retryable": false, "authorizationBoundary": "active_task", "operationReceiptId": "receipt_semantic_future", "currentRevision": 1,
"futureEnvelopeHint": { "version": "1.1-preview" }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_future_03", "sourceSeq": 3, "sourceInstanceId": "runner_semantic_future",
"sourceKind": "runner", "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future",
"eventType": "run.result.proposed", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:50:00.020Z",
"payload": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "Unknown optional receipt fields were ignored by the v1 projection.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_future" }], "verification": [{ "commandOrCheck": "optional field projection stability", "status": "passed" }],
"attentionRequests": [], "artifacts": []
}
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_future_04", "sourceSeq": 4, "sourceInstanceId": "runner_semantic_future",
"sourceKind": "runner", "runId": "run_semantic_future", "normalizedSessionId": "session_semantic_future", "turnId": "turn_semantic_future",
"eventType": "run.terminal", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T10:50:00.030Z",
"payload": { "schema": "paperclip.prp.terminal.v1", "turnTerminalState": "completed", "runTerminalState": "succeeded", "reportedWorkDisposition": "done", "workAssessmentId": "assessment_semantic_future", "statusDecisionId": "decision_semantic_future" }
}
],
"requests": [],
"result": {
"schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "Unknown optional receipt fields were ignored by the v1 projection.",
"completionClaim": { "contractRevision": "semantic-receipts-v1", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] },
"evidence": [{ "receiptId": "receipt_semantic_future" }], "verification": [{ "commandOrCheck": "optional field projection stability", "status": "passed" }],
"attentionRequests": [], "artifacts": []
}
}

View File

@ -0,0 +1,55 @@
{
"schema": "paperclip.prp.fixture.v1",
"fixtureVersion": 1,
"protocolVersion": 1,
"name": "Unsupported semantic tool envelope version",
"description": "A required semantic_tool v2 envelope must fail closed in a PRP v1 consumer.",
"identity": {
"schema": "paperclip.prp.identity.v1", "companyId": "company_semantic_v2", "issueId": "issue_semantic_v2", "runId": "run_semantic_v2",
"environmentLeaseId": "lease_semantic_v2", "runnerInstanceId": "runner_semantic_v2", "normalizedSessionId": "session_semantic_v2", "driverSessionId": "driver_semantic_v2"
},
"capabilities": {
"schema": "paperclip.prp.capabilities.v1", "sessionReusePolicy": "new_per_run", "driver": { "kind": "provider-neutral", "version": "1.0.0" },
"steer": false, "interrupt": true, "resume": true, "runtimeRequests": false, "structuredResult": true, "typedEvents": true, "unsupported": []
},
"commands": [],
"events": [
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_v2_01", "sourceSeq": 1, "sourceInstanceId": "runner_semantic_v2",
"sourceKind": "runner", "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2", "itemId": "item_semantic_v2",
"eventType": "mcp_app.tool_input", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T11:00:00.000Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v2", "schemaVersion": 2, "phase": "input", "operationId": "get_task_context", "callId": "call_semantic_v2",
"correlation": { "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2", "itemId": "item_semantic_v2" },
"idempotencyKey": null,
"content": { "digest": "sha256:9999999999999999999999999999999999999999999999999999999999999999", "redactionDisposition": "digest_only", "references": [] }
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_v2_02", "sourceSeq": 2, "sourceInstanceId": "runner_semantic_v2",
"sourceKind": "runner", "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2", "itemId": "item_semantic_v2",
"eventType": "mcp_app.tool_result", "schemaVersion": 1, "priority": 1, "emittedAt": "2026-08-11T11:00:00.010Z",
"payload": { "semantic_tool": {
"schema": "paperclip.prp.semantic_tool.v1", "schemaVersion": 1, "phase": "result", "operationId": "get_task_context", "callId": "call_semantic_v2",
"correlation": { "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2", "itemId": "item_semantic_v2" },
"idempotencyKey": null,
"content": { "digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "redactionDisposition": "digest_only", "references": [] },
"outcome": "succeeded", "code": "ok", "retryable": false, "authorizationBoundary": "active_task", "operationReceiptId": "receipt_semantic_v2"
} }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_v2_03", "sourceSeq": 3, "sourceInstanceId": "runner_semantic_v2",
"sourceKind": "runner", "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2",
"eventType": "run.result.proposed", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T11:00:00.020Z",
"payload": { "schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "This result must not replay.", "completionClaim": { "contractRevision": "semantic-receipts-v2", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] }, "evidence": [], "verification": [], "attentionRequests": [], "artifacts": [] }
},
{
"schema": "paperclip.prp.event.v1", "sourceEventId": "semantic_v2_04", "sourceSeq": 4, "sourceInstanceId": "runner_semantic_v2",
"sourceKind": "runner", "runId": "run_semantic_v2", "normalizedSessionId": "session_semantic_v2", "turnId": "turn_semantic_v2",
"eventType": "run.terminal", "schemaVersion": 1, "priority": 0, "emittedAt": "2026-08-11T11:00:00.030Z",
"payload": { "schema": "paperclip.prp.terminal.v1", "turnTerminalState": "completed", "runTerminalState": "succeeded", "reportedWorkDisposition": "done" }
}
],
"requests": [],
"result": { "schema": "paperclip.run_result.v1", "reportedWorkDisposition": "done", "summary": "This result must not replay.", "completionClaim": { "contractRevision": "semantic-receipts-v2", "objectiveSatisfied": true, "criteria": [], "remainingWork": [] }, "evidence": [], "verification": [], "attentionRequests": [], "artifacts": [] }
}

View File

@ -80,7 +80,7 @@
{
"path": "schemas/semantic-tool.schema.json",
"id": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json",
"sha256": "f53f6832c0d65caaccf540662fdb08cf62d9bab45a18def7b559c4068ef54efc"
"sha256": "72dba6b17be1358160633e2ac430002b8be1ab311a236289a942cb05b21385da"
},
{
"path": "schemas/stop-reason.schema.json",
@ -235,6 +235,66 @@
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-artifact-happy-path.snapshot.json",
"sha256": "78d61ea44793714dc652b86e0db8b2fdfd8e7f8617f03de6bd88904f51b20491",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-artifact-happy-path.summary.json",
"sha256": "8b3e72e071f7458e67180fdd9933bb89cb6458f2507f2706000c1f172d1e529f",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-conflict-duplicate-retry.snapshot.json",
"sha256": "c236827d1cadd5841a058c3f5ec0d6a542a4676b2ce65dee65e8435216266545",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-conflict-duplicate-retry.summary.json",
"sha256": "f0b277d37c422b90d2012ad61ea5fbe214ebf1607f1a6c2419c518c4f9a52b7e",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-denial-redaction.snapshot.json",
"sha256": "368678297d7763ea5d5d453afa9369e22484ecfc70de7f5769f4cbb05c372565",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-denial-redaction.summary.json",
"sha256": "b0379c2ea8d30e9c2866d3737d9fdc73711de24a955364bd78b75769d925764f",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-governance-wake-monitor.snapshot.json",
"sha256": "4c5f747c497d1a4a1ba1af4e7efaf3951dafc8e095f8f9816fa868dac03803f6",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-governance-wake-monitor.summary.json",
"sha256": "a20a038c04e3f023f7eb478b67144fca48dd288cf484e74d66810d92debc62e5",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-unknown-optional-envelope.snapshot.json",
"sha256": "27fbc947268d2b2a5a60127cd6b575a820b235bc7bce5ec7ade6a6196d4652b6",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/semantic-tool-unknown-optional-envelope.summary.json",
"sha256": "51fb686791aa158f9a23a0c2b90ca24d43bcc953add9e3277ab3f01b958fc26a",
"expectation": "accept",
"compatibilityCase": "deterministic-replay-oracle"
},
{
"path": "fixtures/replay/golden/source-gap.snapshot.json",
"sha256": "7db7f25e423a721e60adfcdfee02db6d5e6fcd4a644ee180f5e29810ebbc0d78",
@ -271,6 +331,42 @@
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-artifact-happy-path.json",
"sha256": "17e4e7ef03563e26111d028b056985c80a760bf6d45726a4e30bc09c1a43ae38",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-conflict-duplicate-retry.json",
"sha256": "8298d73d1be7e54522e1947a6b029f6e58a5a41d4cc190a335ac50d2199d1a2e",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-denial-redaction.json",
"sha256": "4409606c6ae1becd2708f4ff7c5ca82921901cbd0d5dfada171238b032e5343c",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-governance-wake-monitor.json",
"sha256": "56f14ca3284e01894ee8ee70b26d4cdc1181d4e09362e3aa89170fb81f4537ff",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-unknown-optional-envelope.json",
"sha256": "53b90800c7d49877892b4e7b0a3bde21bdda6faa55ac89a008a2ff5420e79d85",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/replay/semantic-tool-unsupported-required-version.json",
"sha256": "ae1a1a873133f11e18f94b0005318000963e1c747dc4e85ae2dfdc08570aadfa",
"expectation": "reject",
"compatibilityCase": "unknown-required-version"
},
{
"path": "fixtures/replay/source-gap.json",
"sha256": "d8a3eb2861e8198226360e666e5296ff5d7215f81d255a273b21d2bfe7dcc2ef",

View File

@ -16,7 +16,7 @@
"properties": {
"schema": { "const": "paperclip.prp.semantic_tool.v1" },
"schemaVersion": { "const": 1 },
"phase": { "enum": ["input", "result"] },
"phase": { "enum": ["input", "result", "reconciled"] },
"operationId": { "$ref": "#/$defs/stableId" },
"callId": { "$ref": "#/$defs/stableId" },
"correlation": {
@ -80,7 +80,10 @@
},
"allOf": [
{
"if": { "properties": { "phase": { "const": "result" } } },
"if": {
"properties": { "phase": { "enum": ["result", "reconciled"] } },
"required": ["phase"]
},
"then": {
"required": [
"outcome",

View File

@ -22,8 +22,10 @@ const protocolRoot = resolve(packageRoot, "protocol");
const schemaDirectory = resolve(protocolRoot, "schemas");
const fixtureDirectory = resolve(protocolRoot, "fixtures");
const outputPath = resolve(protocolRoot, "manifest.json");
const expectedRejectedFixture =
"fixtures/replay/unsupported-required-version.json";
const expectedRejectedFixtures = new Set([
"fixtures/replay/unsupported-required-version.json",
"fixtures/replay/semantic-tool-unsupported-required-version.json",
]);
export async function buildProtocolManifest() {
const schemas = await loadSchemaCatalog(schemaDirectory);
@ -42,7 +44,7 @@ export async function buildProtocolManifest() {
if (relativePath.startsWith("fixtures/replay/golden/")) {
compatibilityCase = "deterministic-replay-oracle";
} else if (relativePath.startsWith("fixtures/replay/")) {
if (relativePath === expectedRejectedFixture) {
if (expectedRejectedFixtures.has(relativePath)) {
expectation = "reject";
compatibilityCase = "unknown-required-version";
try {

View File

@ -18,6 +18,11 @@ const fixtureNames = [
"duplicate-event",
"source-gap",
"unknown-optional-fields",
"semantic-tool-artifact-happy-path",
"semantic-tool-denial-redaction",
"semantic-tool-conflict-duplicate-retry",
"semantic-tool-governance-wake-monitor",
"semantic-tool-unknown-optional-envelope",
];
const check = process.argv.includes("--check");
const stale = [];

View File

@ -170,6 +170,10 @@ export function assertReplayFixtureCompatibility(fixture) {
for (const [index, event] of fixture.events.entries()) {
requireSchema(event, "paperclip.prp.event.v1", `events[${index}]`);
requireVersion(event.schemaVersion, SUPPORTED_EVENT_SCHEMA_VERSION, `events[${index}].schemaVersion`);
const semanticToolVersion = event.payload?.semantic_tool?.schemaVersion;
if (semanticToolVersion !== undefined) {
requireVersion(semanticToolVersion, 1, `events[${index}].payload.semantic_tool.schemaVersion`);
}
}
requireSchema(fixture.result, "paperclip.run_result.v1", "result");

View File

@ -19,6 +19,7 @@ export {
export type { DurableRecoveryIdentity } from "./control-plane/prp-transport-types.js";
export * from "./protocol/replay-contract.js";
export * from "./protocol/result-normalization.js";
export * from "./protocol/semantic-tool-receipts.js";
export * from "./provider-events.js";
export * from "./reducer/session-reducer.js";
export * from "./semantic-tools/index.js";

View File

@ -1866,7 +1866,8 @@ export const semanticToolSchema = {
"phase": {
"enum": [
"input",
"result"
"result",
"reconciled"
]
},
"operationId": {
@ -2002,9 +2003,15 @@ export const semanticToolSchema = {
"if": {
"properties": {
"phase": {
"const": "result"
"enum": [
"result",
"reconciled"
]
}
}
},
"required": [
"phase"
]
},
"then": {
"required": [

View File

@ -19,6 +19,11 @@ const validFixtures = [
"duplicate-event.json",
"source-gap.json",
"unknown-optional-fields.json",
"semantic-tool-artifact-happy-path.json",
"semantic-tool-denial-redaction.json",
"semantic-tool-conflict-duplicate-retry.json",
"semantic-tool-governance-wake-monitor.json",
"semantic-tool-unknown-optional-envelope.json",
];
async function readFixture(
@ -29,6 +34,31 @@ async function readFixture(
) as Record<string, unknown>;
}
function reconciledEvent(
events: Array<Record<string, unknown>>,
): Record<string, unknown> {
const reconciled = structuredClone(events[0]!);
reconciled.sourceEventId = "semantic_happy_reconciled";
reconciled.sourceSeq = 2;
reconciled.eventType = "semantic_tool.reconciled";
const payload = reconciled.payload as Record<string, unknown>;
const semanticTool = payload.semantic_tool as Record<string, unknown>;
const resultPayload = events[1]!.payload as Record<string, unknown>;
const result = resultPayload.semantic_tool as Record<string, unknown>;
semanticTool.phase = "reconciled";
for (const field of [
"content",
"outcome",
"code",
"retryable",
"authorizationBoundary",
"operationReceiptId",
]) {
semanticTool[field] = structuredClone(result[field]);
}
return reconciled;
}
describe("PRP v1 JSON Schema contract", () => {
for (const fixtureName of validFixtures) {
it(`validates ${fixtureName}`, async () => {
@ -73,6 +103,169 @@ describe("PRP v1 JSON Schema contract", () => {
});
});
it("fails closed on an unsupported required semantic-tool version", async () => {
const result = parsePrpFixtureText(
await readFile(
new URL(
"semantic-tool-unsupported-required-version.json",
fixtureDirectory,
),
"utf8",
),
);
expect(result).toMatchObject({
ok: false,
issues: [
{
code: "unsupported_required_version",
path: "/events/0/payload/semantic_tool/schemaVersion",
},
],
});
});
it("accepts a pending semantic call closed by reconciliation alone", async () => {
const fixture = await readFixture("semantic-tool-artifact-happy-path.json");
const events = fixture.events as Array<Record<string, unknown>>;
const reconciled = reconciledEvent(events);
events[1] = reconciled;
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: true,
});
const semanticTool = (
reconciled.payload as Record<string, unknown>
).semantic_tool as Record<string, unknown>;
delete semanticTool.outcome;
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: false,
issues: expect.arrayContaining([
expect.objectContaining({ code: "schema_validation" }),
]),
});
});
it("binds reconciliation identity while preserving recovered result content", async () => {
const fixture = await readFixture("semantic-tool-artifact-happy-path.json");
const events = fixture.events as Array<Record<string, unknown>>;
const reconciled = reconciledEvent(events);
const payload = reconciled.payload as Record<string, unknown>;
const semanticTool = payload.semantic_tool as Record<string, unknown>;
events[1] = reconciled;
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: true,
});
expect(
(semanticTool.content as Record<string, unknown>).digest,
).toBe("sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
semanticTool.operationId = "different_operation";
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: false,
issues: [
expect.objectContaining({
path: "/events/1/payload/semantic_tool/operationId",
}),
],
});
semanticTool.operationId = (
(events[0]!.payload as Record<string, unknown>).semantic_tool as Record<
string,
unknown
>
).operationId;
reconciled.turnId = "different-turn";
(semanticTool.correlation as Record<string, unknown>).turnId =
"different-turn";
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: false,
issues: [
expect.objectContaining({
path: "/events/1/payload/semantic_tool/correlation/turnId",
}),
],
});
});
it(
"allows reconciliation to omit optional correlation metadata",
async () => {
const fixture = await readFixture(
"semantic-tool-artifact-happy-path.json",
);
const events = fixture.events as Array<Record<string, unknown>>;
const input = (events[0]!.payload as Record<string, unknown>)
.semantic_tool as Record<string, unknown>;
const inputCorrelation = input.correlation as Record<string, unknown>;
inputCorrelation.requestId = "request_semantic_happy";
inputCorrelation.futureTraceId = "trace_semantic_happy";
const reconciled = reconciledEvent(events);
const reconciledEnvelope = (
reconciled.payload as Record<string, unknown>
).semantic_tool as Record<string, unknown>;
const reconciledCorrelation = reconciledEnvelope.correlation as Record<
string,
unknown
>;
delete reconciledCorrelation.requestId;
delete reconciledCorrelation.futureTraceId;
events[1] = reconciled;
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: true,
});
},
);
it(
"preserves replacement-runner provenance during reconciliation",
async () => {
const fixture = await readFixture(
"semantic-tool-artifact-happy-path.json",
);
const events = fixture.events as Array<Record<string, unknown>>;
const reconciled = reconciledEvent(events);
const inputSourceInstanceId = events[0]!.sourceInstanceId;
reconciled.sourceInstanceId = "runner_semantic_other";
events[1] = reconciled;
const result = parsePrpFixtureText(JSON.stringify(fixture));
expect(result).toMatchObject({ ok: true });
if (result.ok) {
expect(result.fixture.events[0]?.sourceInstanceId).toBe(
inputSourceInstanceId,
);
expect(result.fixture.events[1]?.sourceInstanceId).toBe(
"runner_semantic_other",
);
}
},
);
it("rejects result and reconciliation as two terminal phases for one call", async () => {
const fixture = await readFixture("semantic-tool-artifact-happy-path.json");
const events = fixture.events as Array<Record<string, unknown>>;
const reconciled = reconciledEvent(events);
for (const event of events.slice(1)) {
event.sourceSeq = Number(event.sourceSeq) + 1;
}
events.splice(1, 0, reconciled);
expect(parsePrpFixtureText(JSON.stringify(fixture))).toMatchObject({
ok: false,
issues: [
expect.objectContaining({
path: "/events/0/payload/semantic_tool/callId",
message: expect.stringContaining("exactly one result or reconciled"),
}),
],
});
});
it("fails closed on unsupported nested required schema versions", async () => {
const fixture = await readFixture();
const events = fixture.events as Array<Record<string, unknown>>;

View File

@ -207,14 +207,20 @@ function ajvIssue(error: ErrorObject): ProtocolValidationIssue {
};
}
interface SemanticCallBinding {
envelope: PrpSemanticToolEnvelope;
index: number;
}
function bindingIssues(fixture: PrpFixture): ProtocolValidationIssue[] {
const issues: ProtocolValidationIssue[] = [];
const uniqueEvents = new Map<string, PrpEvent>();
const semanticCalls = new Map<
string,
{
input?: { envelope: PrpSemanticToolEnvelope; index: number };
result?: { envelope: PrpSemanticToolEnvelope; index: number };
input?: SemanticCallBinding;
result?: SemanticCallBinding;
reconciled?: SemanticCallBinding;
}
>();
fixture.events.forEach((event, index) => {
@ -283,32 +289,77 @@ function bindingIssues(fixture: PrpFixture): ProtocolValidationIssue[] {
message: `semantic_tool call ${semanticTool.callId} must contain exactly one ${phase} envelope`,
});
} else {
call[phase] = { envelope: semanticTool, index };
call[phase] = {
envelope: semanticTool,
index,
};
semanticCalls.set(semanticTool.callId, call);
}
}
});
for (const [callId, call] of semanticCalls) {
if (call.input === undefined || call.result === undefined) {
const present = call.input ?? call.result;
const terminalPhaseCount =
Number(call.result !== undefined) + Number(call.reconciled !== undefined);
if (call.input === undefined || terminalPhaseCount !== 1) {
const present = call.input ?? call.result ?? call.reconciled;
issues.push({
code: "binding_mismatch",
path: `/events/${present?.index ?? 0}/payload/semantic_tool/callId`,
message: `semantic_tool call ${callId} must contain one input and one result envelope`,
message: `semantic_tool call ${callId} must contain one input and exactly one result or reconciled envelope`,
});
continue;
}
for (const field of ["operationId", "idempotencyKey"] as const) {
if (
canonicalJson(call.input.envelope[field]) !==
canonicalJson(call.result.envelope[field])
) {
issues.push({
code: "binding_mismatch",
path: `/events/${call.result.index}/payload/semantic_tool/${field}`,
message: `semantic_tool result ${field} must match its input envelope`,
});
if (call.result !== undefined) {
for (const field of [
"operationId",
"idempotencyKey",
"correlation",
] as const) {
if (
canonicalJson(call.input.envelope[field]) !==
canonicalJson(call.result.envelope[field])
) {
issues.push({
code: "binding_mismatch",
path: `/events/${call.result.index}/payload/semantic_tool/${field}`,
message: `semantic_tool result ${field} must match its input envelope`,
});
}
}
}
if (call.reconciled !== undefined) {
// A replacement runner may reconcile a call after recovering the run.
// The authenticated ingestion boundary owns runner authorization, while
// replay keeps each event's sourceInstanceId as immutable provenance.
for (const field of ["operationId", "idempotencyKey"] as const) {
if (
canonicalJson(call.input.envelope[field]) !==
canonicalJson(call.reconciled.envelope[field])
) {
issues.push({
code: "binding_mismatch",
path: `/events/${call.reconciled.index}/payload/semantic_tool/${field}`,
message: `semantic_tool reconciled ${field} must match its input envelope`,
});
}
}
for (const field of [
"runId",
"normalizedSessionId",
"turnId",
"itemId",
] as const) {
if (
call.input.envelope.correlation[field] !==
call.reconciled.envelope.correlation[field]
) {
issues.push({
code: "binding_mismatch",
path: `/events/${call.reconciled.index}/payload/semantic_tool/correlation/${field}`,
message: `semantic_tool reconciled correlation ${field} must match its input envelope`,
});
}
}
}
}

View File

@ -0,0 +1,149 @@
import { describe, expect, it } from "vitest";
import { validatePrpEvent, type PrpEvent } from "./replay-contract.js";
import {
createPrpBudgetStopReason,
createPrpSemanticToolInputEnvelope,
createPrpSemanticToolReconciledEnvelope,
createPrpSemanticToolResultEnvelope,
semanticAuthorizationBoundaryForCode,
} from "./semantic-tool-receipts.js";
const correlation = {
runId: "run_receipt_test",
normalizedSessionId: "session_receipt_test",
turnId: "turn_receipt_test",
itemId: "item_receipt_test",
};
describe("PRP provider-neutral semantic receipts", () => {
it("digests raw content and retains only allowlisted references", () => {
const envelope = createPrpSemanticToolInputEnvelope({
operationId: "get_task_context",
callId: "call_receipt_test",
correlation,
content: { authorization: "Bearer secret-token", apiKey: "do-not-emit" },
references: [{ kind: "task", id: "issue_receipt_test" }],
});
expect(envelope.content).toMatchObject({
digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
redactionDisposition: "digest_only",
references: [{ kind: "task", id: "issue_receipt_test" }],
});
expect(JSON.stringify(envelope)).not.toContain("secret-token");
expect(JSON.stringify(envelope)).not.toContain("do-not-emit");
});
it("gives exact retries the same operation receipt without provider identifiers", () => {
const base = {
operationId: "write_document",
correlation,
idempotencyKey: "write_once",
content: { revisionId: "revision_1" },
outcome: "succeeded" as const,
code: "ok",
retryable: false,
authorizationBoundary: "revision" as const,
};
const first = createPrpSemanticToolResultEnvelope({ ...base, callId: "call_first" });
const retry = createPrpSemanticToolResultEnvelope({ ...base, callId: "call_retry" });
expect(retry.operationReceiptId).toBe(first.operationReceiptId);
expect(retry.operationReceiptId).toMatch(/^semantic_receipt:[a-f0-9]{32}$/);
expect(retry).not.toHaveProperty("provider");
});
it("validates semantic input/result events and a terminal stop receipt", () => {
const input = createPrpSemanticToolInputEnvelope({
operationId: "get_task_context",
callId: "call_receipt_test",
correlation,
content: {},
});
const result = createPrpSemanticToolResultEnvelope({
operationId: "get_task_context",
callId: "call_receipt_test",
correlation,
content: {},
outcome: "succeeded",
code: "ok",
retryable: false,
authorizationBoundary: "active_task",
});
expect(validatePrpEvent(event("mcp_app.tool_input", input)).ok).toBe(true);
expect(validatePrpEvent(event("mcp_app.tool_result", result)).ok).toBe(true);
expect(
createPrpBudgetStopReason({
receiptId: "stop_receipt_test",
kind: "cost",
code: "run_cost_limit",
retryable: true,
decisionId: "decision_receipt_test",
limitClass: "run_cost",
aggregate: { unit: "usd_micros", observed: 100, limit: 100, window: "run" },
}),
).toMatchObject({
schema: "paperclip.prp.stop_reason.v1",
kind: "cost",
decisionId: "decision_receipt_test",
});
});
it("requires a complete terminal receipt when a pending tool call is reconciled", () => {
const semanticTool = createPrpSemanticToolReconciledEnvelope({
operationId: "get_task_context",
callId: "call_reconciled_test",
correlation,
content: {},
outcome: "succeeded",
code: "recovered_receipt",
retryable: false,
authorizationBoundary: "active_task",
});
const reconciled = event("semantic_tool.reconciled", semanticTool);
expect(validatePrpEvent(reconciled).ok).toBe(true);
expect(
validatePrpEvent({
...reconciled,
payload: {
semantic_tool: {
...semanticTool,
outcome: undefined,
},
},
}).ok,
).toBe(false);
});
it("maps denial and conflict codes onto protocol authorization boundaries", () => {
expect(semanticAuthorizationBoundaryForCode("company_scope_denied")).toBe("company");
expect(semanticAuthorizationBoundaryForCode("actor_role_denied")).toBe("actor");
expect(semanticAuthorizationBoundaryForCode("document_revision_conflict")).toBe("revision");
expect(semanticAuthorizationBoundaryForCode("approval_required")).toBe("governed_action");
});
});
function event(
eventType: "mcp_app.tool_input" | "mcp_app.tool_result" | "semantic_tool.reconciled",
semanticTool: Record<string, unknown>,
): PrpEvent {
return {
schema: "paperclip.prp.event.v1",
sourceEventId: `${eventType}:receipt_test`,
sourceSeq: eventType === "mcp_app.tool_input" ? 1 : eventType === "mcp_app.tool_result" ? 2 : 3,
sourceInstanceId: "runner_receipt_test",
sourceKind: "runner",
runId: correlation.runId,
normalizedSessionId: correlation.normalizedSessionId,
turnId: correlation.turnId,
itemId: correlation.itemId,
eventType,
schemaVersion: 1,
priority: 1,
emittedAt: "2026-08-11T12:00:00.000Z",
payload: { semantic_tool: semanticTool },
} as PrpEvent;
}

View File

@ -0,0 +1,253 @@
import { createHash } from "node:crypto";
import type {
PrpEvent,
PrpSemanticToolEnvelope,
PrpStopReason,
} from "./replay-contract.js";
export type PrpSemanticAuthorizationBoundary =
| "company"
| "actor"
| "active_task"
| "grant"
| "governed_action"
| "lock"
| "revision";
export type PrpSemanticToolOutcome =
| "succeeded"
| "denied"
| "conflict"
| "duplicate"
| "unavailable"
| "failed";
export interface PrpSemanticCorrelation {
runId: string;
normalizedSessionId: string;
turnId: string;
itemId: string;
requestId?: string;
}
export interface PrpSemanticSafeReference {
kind:
| "task"
| "document_revision"
| "interaction"
| "approval"
| "decision"
| "artifact"
| "work_product"
| "wake"
| "monitor"
| "audit"
| "operation";
id: string;
}
export interface PrpSemanticImmutableTarget {
kind: "document_revision" | "interaction" | "approval" | "decision";
id: string;
immutable: true;
revisionId?: string;
decisionId?: string;
}
interface SemanticEnvelopeBaseInput {
operationId: string;
callId: string;
correlation: PrpSemanticCorrelation;
idempotencyKey?: string | null;
content: unknown;
references?: readonly PrpSemanticSafeReference[];
redactionDisposition?: "digest_only" | "allowlisted_references" | "redacted";
}
export interface CreatePrpSemanticToolResultInput extends SemanticEnvelopeBaseInput {
outcome: PrpSemanticToolOutcome;
code: string;
retryable: boolean;
authorizationBoundary: PrpSemanticAuthorizationBoundary;
operationReceiptId?: string;
auditReceiptId?: string;
currentRevision?: string | number;
duplicateOfReceiptId?: string;
artifactRefs?: readonly PrpSemanticSafeReference[];
targets?: readonly PrpSemanticImmutableTarget[];
causalRefs?: readonly PrpSemanticSafeReference[];
}
/**
* Creates a safe input envelope: raw content is reduced to a canonical digest,
* while callers may add only typed, non-secret references.
*/
export function createPrpSemanticToolInputEnvelope(
input: SemanticEnvelopeBaseInput,
): PrpSemanticToolEnvelope {
return {
schema: "paperclip.prp.semantic_tool.v1",
schemaVersion: 1,
phase: "input",
operationId: input.operationId,
callId: input.callId,
correlation: { ...input.correlation },
idempotencyKey: input.idempotencyKey ?? null,
content: safeContent(input),
} as PrpSemanticToolEnvelope;
}
/** Creates the provider-neutral semantic result receipt carried on PRP. */
export function createPrpSemanticToolResultEnvelope(
input: CreatePrpSemanticToolResultInput,
): PrpSemanticToolEnvelope {
return createPrpSemanticToolTerminalEnvelope(input, "result");
}
/** Creates a complete terminal receipt for an outcome recovered after restart. */
export function createPrpSemanticToolReconciledEnvelope(
input: CreatePrpSemanticToolResultInput,
): PrpSemanticToolEnvelope {
return createPrpSemanticToolTerminalEnvelope(input, "reconciled");
}
function createPrpSemanticToolTerminalEnvelope(
input: CreatePrpSemanticToolResultInput,
phase: "result" | "reconciled",
): PrpSemanticToolEnvelope {
const operationReceiptId = input.operationReceiptId ?? derivedReceiptId(input);
return {
schema: "paperclip.prp.semantic_tool.v1",
schemaVersion: 1,
phase,
operationId: input.operationId,
callId: input.callId,
correlation: { ...input.correlation },
idempotencyKey: input.idempotencyKey ?? null,
content: safeContent(input),
outcome: input.outcome,
code: input.code,
retryable: input.retryable,
authorizationBoundary: input.authorizationBoundary,
operationReceiptId,
...(input.auditReceiptId === undefined ? {} : { auditReceiptId: input.auditReceiptId }),
...(input.currentRevision === undefined ? {} : { currentRevision: input.currentRevision }),
...(input.duplicateOfReceiptId === undefined
? {}
: { duplicateOfReceiptId: input.duplicateOfReceiptId }),
...(input.artifactRefs === undefined ? {} : { artifactRefs: [...input.artifactRefs] }),
...(input.targets === undefined ? {} : { targets: [...input.targets] }),
...(input.causalRefs === undefined ? {} : { causalRefs: [...input.causalRefs] }),
} as PrpSemanticToolEnvelope;
}
export function createPrpBudgetStopReason(input: {
receiptId: string;
kind: "budget" | "cost";
code: string;
retryable: boolean;
decisionId: string;
limitClass: "company_monthly" | "actor_monthly" | "project_monthly" | "run_cost" | "provider_quota";
aggregate: {
unit: "cents" | "usd_micros" | "tokens";
observed: number;
limit: number;
window: "run" | "monthly_utc" | "provider_window";
};
relatedReceiptIds?: readonly string[];
}): PrpStopReason {
return {
schema: "paperclip.prp.stop_reason.v1",
schemaVersion: 1,
receiptId: input.receiptId,
kind: input.kind,
code: input.code,
retryable: input.retryable,
decisionId: input.decisionId,
limitClass: input.limitClass,
aggregate: { ...input.aggregate },
...(input.relatedReceiptIds === undefined
? {}
: { relatedReceiptIds: [...input.relatedReceiptIds] }),
} as PrpStopReason;
}
/** Returns only typed result receipts; optional provider payload stays ignored. */
export function prpSemanticToolResultReceipts(
events: readonly PrpEvent[],
): PrpSemanticToolEnvelope[] {
return events.flatMap((event) => {
if (event.eventType !== "mcp_app.tool_result") return [];
const payload = asRecord(event.payload);
const envelope = asRecord(payload?.semantic_tool);
if (
envelope?.schema !== "paperclip.prp.semantic_tool.v1"
|| envelope.schemaVersion !== 1
|| envelope.phase !== "result"
) return [];
return [structuredClone(envelope) as PrpSemanticToolEnvelope];
});
}
export function semanticAuthorizationBoundaryForCode(
code: string,
): PrpSemanticAuthorizationBoundary {
if (code.includes("company")) return "company";
if (code.includes("actor") || code.includes("role")) return "actor";
if (code.includes("claim") || code.includes("exposed")) return "grant";
if (code.includes("revision") || code.includes("idempotency_conflict")) return "revision";
if (code.includes("lock") || code.includes("ownership")) return "lock";
if (code.includes("approval") || code.includes("interaction") || code.includes("governed")) {
return "governed_action";
}
return "active_task";
}
export function semanticOutcomeForCode(input: {
ok: boolean;
code: string;
disposition?: unknown;
}): PrpSemanticToolOutcome {
if (input.ok) return input.disposition === "duplicate" ? "duplicate" : "succeeded";
if (input.code.includes("conflict") || input.code.includes("revision")) return "conflict";
if (input.code.includes("unavailable") || input.code.includes("not_exposed")) return "unavailable";
return "denied";
}
export function digestPrpSemanticContent(value: unknown): string {
return `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
}
function safeContent(input: SemanticEnvelopeBaseInput) {
return {
digest: digestPrpSemanticContent(input.content),
redactionDisposition: input.redactionDisposition ?? "digest_only",
references: [...(input.references ?? [])],
};
}
function derivedReceiptId(input: CreatePrpSemanticToolResultInput): string {
const identity = input.idempotencyKey === undefined || input.idempotencyKey === null
? `${input.correlation.runId}:${input.operationId}:${input.callId}`
: `${input.correlation.runId}:${input.operationId}:${input.idempotencyKey}`;
return `semantic_receipt:${createHash("sha256").update(identity).digest("hex").slice(0, 32)}`;
}
function asRecord(value: unknown): Record<string, unknown> | null {
return typeof value === "object" && value !== null && !Array.isArray(value)
? value as Record<string, unknown>
: null;
}
function canonicalJson(value: unknown): string {
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
const record = asRecord(value);
if (record !== null) {
return `{${Object.keys(record)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
.join(",")}}`;
}
return JSON.stringify(value) ?? "null";
}