Files
PaperClipAI/packages/paperclip-runner/protocol
DottaandPaperclip 09162fe893 refactor(runner): clarify native task assignment guidance (#18704)
## Thinking Path

> - Paperclip lets people assign work to AI agents.
> - Native task creation can start the assigned agent as soon as the
task is ready.
> - The default assignee is the agent that creates the task.
> - An agent can omit the requested teammate, create work for itself,
and reassign it too late.
> - The server already supports setting the requested agent and project
during creation.
> - This pull request makes those fields and the early-start risk clear
in both tool catalogs and the delegated-task example.
> - The benefit is clearer guidance for the existing delegation
contract.

## Linked Issues or Issue Description

Related: #13281.

**What happened?**

An observed delegation attempt called `create_task` with only a title
and retry key. The task used the documented current-agent default and
started work before a later reassignment. The server honored its
contract. The original failed QA result remains failed.

**Expected behavior**

When a user names an existing teammate and project, the agent should put
their exact IDs in `assigneeActorId` and `projectId` during task
creation. The requested worker can then start with the correct task
ownership and project.

**Steps to reproduce**

1. Ask an agent to create work for a named existing teammate in a named
project.
2. Create a ready task with only its title and retry key.
3. Observe that the creating agent is assigned and can start before a
later reassignment.

This is a guidance mitigation for agent tool misuse. It does not change
the default or enforce model compliance. A fresh deployed qualification
is still required.

## What Changed

- Explain initial teammate and project selection in the canonical
`create_task` description and its two field descriptions.
- Add a representative delegated-task call with an explicit agent,
project, and saved plan.
- Synchronize the same three descriptions in the sibling semantic-action
catalog. Keep its schema constraints intact.
- Keep the global execution prompt byte-for-byte equal to the base.
Revision `paperclip-execution.v8` already directs the agent to create
delegated work with the teammate ID.
- Refresh the generated catalogs, seeded catalog identity, and protocol
manifest.
- Make the collaboration contract test use the declared scenario
example, as the general contract test already does. Retain separate
live-example validation and strict scenario input checks.

## Verification

- All 79 existing native controls passed across `chat-project-tools`,
`paperclip-runner-tool-authority`, `task-creation-backlog`, and
`tool-receipt-recovery`. These cover alternate-agent readback,
current-agent defaults, backlog holds, company boundaries, and dispatch
recovery after commit.
- All 108 protocol-action, semantic-catalog, catalog-doc,
runtime-context, and collaboration checks passed. All 11 contract
fingerprint checks passed.
- The original CI collaboration-test failure reproduced locally before
correction: the test sent live-only fields to the older scenario schema.
After selecting the declared scenario example, all 13 collaboration
cases passed. Malformed titles, undeclared project fields, and arbitrary
unknown fields still reject. No schema or live example was changed for
this correction.
- Generator checks passed for semantic contracts, the semantic-action
catalog, operation groups, and protocol coverage.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs`, and
`git diff --check` passed.
- A structural comparison confirms that types, required fields, lengths,
modes, permissions, outputs, and defaults are unchanged on both catalog
surfaces.
- No new text-matching tests were added. These local checks passed again
after the global prompt was restored. Fresh exact-head Greptile and
Paperclip reviews are both 5/5 with no actionable findings.
[Current-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41565349812)
completed successfully on `2e5f93e122191e17b316a7780a74ff404fc468f3`
(attempt 2), with all 49 workflow jobs successful.
- Current-head CI first failed only the unchanged native-process
retention case at its original five-second limit. The shard passed 2,130
other tests and skipped eight. One unchanged local Node 24 control
passed. The original test and retention dependency blobs match the base.
The exact timeout cause remains unknown. No source, assertion, or time
limit changed. One authorized recheck of only that runner shard and its
normal final gate passed. The recheck passed 2,131 tests and skipped
eight. The other 47 successful job results were carried forward with
their original start and finish times.
- The prior head `42207172dd4bc376dab880dbccb2c698acccf6c4` passed all
49 CI jobs and fresh 5/5 reviews. Its first CI attempt lost one server
runner to a reported shutdown. One authorized retry of only that lost
shard and its normal gate passed with unchanged assertions and time
limits. The original failure and recovery remain recorded.
- A normal merge of that prior head was rejected by the base policy
because the global prompt required a code-owner review. This revision
drops that optional global change. No policy override or bypass was
used.
- The new catalogs have semantic catalog digest
`sha256:5af904b7fc088509b98dde41611c042ff3c22c881e252190817978de0d1b733a`.
The authorized `create_task` operation digest is
`sha256:f5dad10c7969680d91514603966705ac09ceaa3f2cff21fd6c199e2de0d7a63d`.
- A new run can report admitted prompt revision v8 and its digest. The
ordinary run-profile tool fingerprint does not hash these descriptions.
Source/Core and matching provider-pack identity prove deployed code, not
model delivery or compliance. A fresh campaign with new agents is still
required.
- No model run or deployed acceptance was performed for this source
change. The original failed qualification remains failed. This
tool-guidance change is a mitigation hypothesis.

## Risks

The longer tool descriptions change the catalog identity. A model can
still omit a requested field. The global prompt remains v8. Existing
assignment, authorization, wake, replay, and session behavior stays
intact. A new campaign is required because a stored provider session can
retain old tool declarations. A native-process retention test has a
recurring five-second timeout on unchanged code. One unchanged local
Node 24 control passed. This does not prove CI reliability or explain
the timeout. No timeout or assertion was changed.

## Model Used

OpenAI GPT-6 through Codex with reasoning and code execution. The
runtime does not expose a more specific model identifier or
context-window size.

## 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 (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 20:46:07 -06:00
..

PRP v1/v2 Contract

The JSON Schema files in schemas/ are the language-neutral source of truth for Paperclip Runner Protocol versions 1 and 2. The fixtures in fixtures/ define accepted and rejected compatibility cases.

Compatibility

  • protocolVersion, fixtureVersion, and event.schemaVersion are required.
  • A consumer fails closed when a required version or schema discriminator is not supported.
  • A v1 envelope can contain an unknown optional property when its schema marks that object as extensible.
  • A consumer ignores an unknown optional property until a later contract gives it meaning.
  • A required field, enum value, or typed structured-input field is not optional.
  • Question and answer identifiers are stable across the provider boundary.
  • Peers negotiate the highest mutually supported protocol version. Existing v1 runners remain compatible but cannot receive v2-only session-goal commands.

The unknown-optional-fields.json fixture must be accepted. The unsupported-required-version.json fixture must be rejected.

Session goals (v2)

PRP v2 adds a provider-neutral durable session-goal lifecycle. Every v2 capability snapshot includes sessionGoals, even when its availability is unsupported or policy_disabled. Paperclip sends controls only when the negotiated capability advertises the corresponding action.

Commands:

  • session.goal.get
  • session.goal.set for objective, status, and optional token budget changes
  • session.goal.clear

Events:

  • session.capabilities.updated
  • session.goal.snapshot
  • session.goal.updated
  • session.goal.cleared

Goal state is separate from Paperclip's company/business goal hierarchy. The snapshot distinguishes the durable status from workingNow, because an active goal can be idle between autonomous turns. A runner emits the full capability and authoritative snapshot after every session open or resume. Missing v1 capability is unsupported; clients do not infer support from an adapter name.

Harness credential updates

auth.credentials.updated is a P0 runner event available in the v1, v2, and v3 event schemas. Its payload uses paperclip.auth.credentials.updated.v1 and contains only provider, bindingId, revision, and detection. No tokens, account identifiers, credential paths, or credential fingerprints are permitted.

An updated consumer opts in with observeCredentials: true in run.prepare, before session.open. The bundled runner transport opts in for fresh sessions; set its observeCredentials option to false to disable observation for a fresh session. Older hosts that omit the field receive no new event. Consumers must update their schemas before opting in: an event-enum addition is not an unknown optional property. Existing runner binaries ignore the optional flag and do not provide observation; absence of an event does not establish support.

The current Unix runner observes native Codex's managed CODEX_HOME/auth.json and the Codex/Grok ACPX session homes. It uses a non-recursive directory watch plus a one-second polling fallback, so atomic file replacement is handled. Grok's initial inline subscription credential is the baseline, allowing a refresh during launcher startup to be detected. A first valid credential file without a prior baseline establishes the baseline without claiming a refresh. Account identity changes also emit updates, even when token values stay the same, and establish the baseline for later refreshes on the new account. Hosts must evaluate the current credential through their private credential channel and account checks; the event does not authorize an account switch. Metadata-only changes, partial writes, and deletion do not emit updates. Unsupported harnesses and refreshes retained only in memory need their own observer or native-hook integration; adding this event does not make them observable.

bindingId: "harness-auth" identifies the selected harness authentication within the envelope's normalized session. Revisions increase for that binding and survive runner restarts and warm run attachments. The baseline fingerprint and pending event are persisted together in a private runner state file. Source retries retain the same event identity until the durable PRP outbox accepts it. Hosts should commit an idempotent reaction or durable work item through onCommittedEvent before returning and advancing the cumulative ACK. The bundled transport exposes onCredentialsUpdated(event) for this commit. It awaits the callback before ACK, including during warm idle, attachment, and provider cleanup. Callback failures leave delivery unacknowledged for retry; callbacks must therefore deduplicate by the original source event identity. When configured, credential updates bypass the turn notification queue. Paperclip commits them directly to the run log in a separate credential source namespace, preserving the original PRP envelope and avoiding collisions with the driver's normalized event sequence. A turn-only notification consumer cannot reliably handle idle or cleanup updates; use this callback instead.

detection: "credential_store" reports an observed credential change, not proof that an OAuth refresh succeeded. Multiple changes between observations can be coalesced; this is not an audit of every refresh exchange. The schema also allows native_hook for integrations with an explicit signal. Observation continues during warm idle and checks at turn terminal and close boundaries, including Grok's private refresh handoff after live-store cleanup. Retained runner observations enter the durable outbox before a suspend or shutdown command completes, without draining the provider backlog. A final check after provider cleanup also enters the outbox and waits for the host ACK. The runner retains its terminal recovery fence if that delivery fails.

The event authorizes no credential access or write on its own. A host that needs rotated token values must use a separately authorized handoff with its existing account-identity and freshness safeguards. The default Paperclip credential copy-back behavior remains in place.

Scope

The first provider descriptor and adapter fixture cover Codex only. The schemas for provider-neutral events and semantic receipts do not enable those actions. Discovery and authorization are separate contracts.

The conformance manifest records every source file and its SHA-256 digest. Run pnpm generate:protocol-manifest from this package after a source change. CI runs the same generator with --check to reject drift. This check also compiles the JSON Schemas and validates every replay, question, and cross-language conformance fixture against its declared schema.

The files in fixtures/replay/golden/ are deterministic reducer oracles. Each accepted replay fixture has a complete session snapshot and a compact parity summary. pnpm generate:replay-goldens updates them after an intentional reducer change; package build and CI fail when they drift.

The files in fixtures/local-runner/scripts/ drive the package-local fake harness. They cover successful, failed, interrupted, interactive, duplicate terminal, process-cleanup, and oversized-frame behavior without starting a production adapter.

runtime.activity.recorded is a P1 Runner event in v1, v2 and v3 with exactly { namespace, activityKey }. Both opaque identifiers contain 1–160 Unicode code points and exclude C0 controls and DEL. namespace groups a producer's activity types; activityKey names an activity within that group. Consumers use the pair to select a response to activity in the run. The authenticated outer envelope provides run and session identity, source sequence and emission time. Core resolves the run's company, task and agent through its existing authorized run binding; callers cannot supply those identities in the payload. Recording activity does not independently verify the reported action or grant access to any resource. The event uses the ordinary durable outbox, acknowledgement and replay contract.

Local clients capture an opaque context with paperclip-runnerd notification-context before an operation and invoke record-activity CONTEXT NAMESPACE ACTIVITY_KEY after success. Runner publishes its matching absolute executable as host-owned PAPERCLIP_RUNTIME_NOTIFICATION_HELPER and the stable endpoint descriptor path as PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT. Configured task environment cannot override either value. Context is invalidated on warm attachment and process recovery, so a caller cannot attribute a later completion to a different run.