Files
PaperClipAI/packages/paperclip-runner/protocol/schemas/semantic-tool.schema.json
Dotta e18632ebcb 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
2026-08-29 21:48:06 -05:00

168 lines
4.6 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json",
"title": "PRP provider-neutral semantic tool envelope",
"type": "object",
"required": [
"schema",
"schemaVersion",
"phase",
"operationId",
"callId",
"correlation",
"idempotencyKey",
"content"
],
"properties": {
"schema": { "const": "paperclip.prp.semantic_tool.v1" },
"schemaVersion": { "const": 1 },
"phase": { "enum": ["input", "result", "reconciled"] },
"operationId": { "$ref": "#/$defs/stableId" },
"callId": { "$ref": "#/$defs/stableId" },
"correlation": {
"type": "object",
"required": ["runId", "normalizedSessionId", "turnId", "itemId"],
"properties": {
"runId": { "$ref": "#/$defs/stableId" },
"normalizedSessionId": { "$ref": "#/$defs/stableId" },
"turnId": { "$ref": "#/$defs/stableId" },
"itemId": { "$ref": "#/$defs/stableId" },
"requestId": { "$ref": "#/$defs/stableId" }
},
"additionalProperties": true
},
"idempotencyKey": {
"type": ["string", "null"],
"minLength": 1,
"maxLength": 240
},
"content": { "$ref": "#/$defs/safeContent" },
"outcome": {
"enum": ["succeeded", "denied", "conflict", "duplicate", "unavailable", "failed"]
},
"code": { "$ref": "#/$defs/stableCode" },
"retryable": { "type": "boolean" },
"authorizationBoundary": {
"enum": ["company", "actor", "active_task", "grant", "governed_action", "lock", "revision"]
},
"operationReceiptId": { "$ref": "#/$defs/stableId" },
"auditReceiptId": { "$ref": "#/$defs/stableId" },
"currentRevision": {
"oneOf": [
{ "type": "integer", "minimum": 0 },
{ "$ref": "#/$defs/stableId" }
]
},
"duplicateOfReceiptId": { "$ref": "#/$defs/stableId" },
"artifactRefs": {
"type": "array",
"items": {
"allOf": [
{ "$ref": "#/$defs/safeReference" },
{
"type": "object",
"properties": { "kind": { "enum": ["artifact", "work_product"] } }
}
]
},
"uniqueItems": true
},
"targets": {
"type": "array",
"items": { "$ref": "#/$defs/immutableTarget" },
"uniqueItems": true
},
"causalRefs": {
"type": "array",
"items": { "$ref": "#/$defs/safeReference" },
"uniqueItems": true
}
},
"allOf": [
{
"if": {
"properties": { "phase": { "enum": ["result", "reconciled"] } },
"required": ["phase"]
},
"then": {
"required": [
"outcome",
"code",
"retryable",
"authorizationBoundary",
"operationReceiptId"
]
}
}
],
"additionalProperties": true,
"$defs": {
"stableId": {
"type": "string",
"minLength": 1,
"maxLength": 240,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
},
"stableCode": {
"type": "string",
"minLength": 1,
"maxLength": 160,
"pattern": "^[a-z][a-z0-9_.:-]*$"
},
"safeContent": {
"type": "object",
"required": ["digest", "redactionDisposition", "references"],
"properties": {
"digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"redactionDisposition": {
"enum": ["digest_only", "allowlisted_references", "redacted"]
},
"references": {
"type": "array",
"items": { "$ref": "#/$defs/safeReference" },
"uniqueItems": true
}
},
"additionalProperties": true
},
"safeReference": {
"type": "object",
"required": ["kind", "id"],
"properties": {
"kind": {
"enum": [
"task",
"document_revision",
"interaction",
"approval",
"decision",
"artifact",
"work_product",
"wake",
"monitor",
"audit",
"operation"
]
},
"id": { "$ref": "#/$defs/stableId" }
},
"additionalProperties": true
},
"immutableTarget": {
"type": "object",
"required": ["kind", "id", "immutable"],
"properties": {
"kind": { "enum": ["document_revision", "interaction", "approval", "decision"] },
"id": { "$ref": "#/$defs/stableId" },
"immutable": { "const": true },
"revisionId": { "$ref": "#/$defs/stableId" },
"decisionId": { "$ref": "#/$defs/stableId" }
},
"additionalProperties": true
}
}
}