mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 21:49:18 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native ACPX runs admit a provider only when its saved profile version and `commandDigest` match the current profile exactly. > - #18325 made every provider digest attest its upstream release, and bound the shared ACPX layer and Runner source through the pack `contractDigest`. > - The model is documented, but it lived in prose. A new provider, such as one in an open PR, could still choose another digest model without any check or guidance. > - This pull request makes the model the default path: each provider declares its attestation in one registry, and the generator, a test and a scoped rule explain the pattern where contributors meet it. > - The benefit is that new providers and upstream bumps follow one model, without a growing list of blocked legacy patterns. ## Linked Issues or Issue Description Refs #18325, #18237. **What happened?** After #18325, only Copilot, Grok and Pi went through `acpx-release-attestations.mjs`. Cursor and Antigravity had separate inline checks in the generator, and Claude and Codex had none. A new provider could add a profile with any digest. Nothing told the author how to compute it, and nothing pinned the profile versions that now change only for deliberate resets. **Expected behavior** Every ACPX provider declares how its `commandDigest` attests the upstream release. Tools and review guidance describe the pattern when a contributor adds a provider, bumps an upstream release or changes a version. **Steps to reproduce** 1. Add a new provider profile to `packages/paperclip-runner/acpx-profiles.json` with an arbitrary `commandDigest`. 2. Run `node packages/paperclip-runner/scripts/generate-acpx-profiles.mjs --check`. 3. Before this change, the check passes. ## What Changed - Add `ACPX_RELEASE_ATTESTATIONS` to `scripts/acpx-release-attestations.mjs`. Every profile declares one kind: - `installed-command` (Claude, Codex): installation hashes the upstream launch command. - `launcher` (Grok): the SHA-256 of the launcher shipped verbatim. - `closure` (Copilot, Pi): upstream versions and complete platform closure pins. - `declaration` (Cursor, Antigravity): a checked-in release declaration, as canonical JSON. - `generate-acpx-profiles.mjs` checks every profile through the registry. The separate Cursor and Antigravity digest checks are now part of the same path. No digest changes. - A profile without a declared attestation fails generation. The message says to declare one and points to the rule. - A stale digest message now says that a digest changes only with its upstream release, and that an upstream change keeps the profile version. - Add a test that pins the profile versions. It explains that a bump resets every saved session for that provider, and that a new provider starts at 1. - Add `rules/acpx-profile-identity.mdc`. It describes the identity model, how to add a provider, when to bump a version, and review criteria. `AGENTS.md` and `.greptile/files.json` route the profile, attestation, contract-registry, ACPX driver and provider paths to it. - Update the profile identity section in `packages/paperclip-runner/docs/durable-recovery.md` with the attestation kinds and the version policy. ## Verification - In `packages/paperclip-runner`: - `node scripts/generate-acpx-profiles.mjs --check` passes. Running it without `--check` changes no digest. - `node --test scripts/acpx-release-attestations.test.mjs scripts/contracts/contract-fingerprints.test.mjs`: 22 tests pass. New cases: every profile declares an attestation, a new provider without one fails with guidance, and Cursor and Antigravity declaration changes affect only their own digests. - `vitest run src/drivers/acpx/profile-compatibility.test.ts`: 15 tests pass, including the version table. - `tsc -p tsconfig.json --noEmit` passes. ## Risks - Low risk. No digest, version or runtime admission behavior changes. - A new provider PR must add one registry entry. The generator error states what to add. - The version table needs an edit for each deliberate reset. Upstream bumps do not touch it. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, file edits, local test runs). ## 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 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
4.3 KiB
Plaintext
73 lines
4.3 KiB
Plaintext
---
|
|
description: Give every ACPX provider the same identity model - its commandDigest attests the upstream release, the pack contract digest covers Runner source and the shared ACPX layer, and profile versions change only for deliberate resets.
|
|
globs: "packages/paperclip-runner/acpx-profiles.json,packages/paperclip-runner/cursor-contract.json,packages/paperclip-runner/cursor-distributions.json,packages/paperclip-runner/antigravity-distributions.json,packages/paperclip-runner/scripts/acpx-release-attestations.mjs,packages/paperclip-runner/scripts/generate-acpx-profiles.mjs,packages/paperclip-runner/scripts/contracts/**,packages/paperclip-runner/src/drivers/acpx/**,packages/paperclip-runner/src/providers/**,packages/paperclip-runner/protocol/**"
|
|
alwaysApply: false
|
|
---
|
|
|
|
# ACPX profile identity
|
|
|
|
## Scope and purpose
|
|
|
|
This rule covers ACPX provider profiles, their release declarations and pins,
|
|
the profile generator, the provider-pack contract registry, and the ACPX
|
|
driver and provider code. Every ACPX harness (Claude, Codex, Copilot, Cursor,
|
|
Grok, Pi, Antigravity and any new one) uses the same identity model, so a
|
|
change behaves the same way for all of them.
|
|
|
|
## The identity model
|
|
|
|
A provider profile in `acpx-profiles.json` has a version and a `commandDigest`.
|
|
Three things decide whether saved work and installed packs stay compatible:
|
|
|
|
| What changed | What carries it | Effect |
|
|
| --- | --- | --- |
|
|
| The provider's upstream release: package versions, platform closures, a launcher shipped verbatim | The profile's `commandDigest` | Saved sessions for that provider start fresh |
|
|
| The shared ACPX layer | The profile's `acpxVersion` and the Runner's ACPX patch in the pack `contractDigest` | The same for every provider |
|
|
| A runner/pack contract: schemas, generated catalogs, Rust shapes and env keys | The provider pack's `contractDigest` | Older image packs are not reused |
|
|
|
|
Ordinary Runner source changes, refactors and comments change none of these.
|
|
|
|
## Required behavior
|
|
|
|
- **Attest the upstream release.** Declare each provider's attestation in
|
|
`ACPX_RELEASE_ATTESTATIONS` (`scripts/acpx-release-attestations.mjs`), using
|
|
the kind that fits: `installed-command`, `launcher`, `closure` or
|
|
`declaration`. The digest binds only what a repackager copies verbatim from
|
|
the upstream release, so the same release gives the same digest wherever the
|
|
pack is built or installed.
|
|
- **Keep Paperclip source and the ACPX layer out of provider digests.**
|
|
Runner/pack compatibility belongs to the `contractDigest`. When Runner code
|
|
and a pack must agree on a new field or behavior, declare it in a schema
|
|
under `protocol/` and generate the TypeScript, so the contract registry
|
|
covers it.
|
|
- **Regenerate instead of hand-editing.** After an upstream bump or a pin
|
|
change, run `pnpm --filter @paperclipai/paperclip-runner generate:acpx-profiles`.
|
|
`check:acpx-profiles` runs in the build and refuses a stale digest.
|
|
- **Keep the profile version.** An upstream release changes the digest, not the
|
|
version. A new provider starts at version 1. Bump a version only to reset
|
|
every saved session for that provider on purpose, such as a change in saved
|
|
session semantics that exact admission cannot detect. Record the reason in
|
|
the PR and in the version table in `profile-compatibility.test.ts`.
|
|
- **Keep earlier versions decodable.** Saved inputs with any version from 1
|
|
through the current one decode, so recovery can refuse them. Launch and
|
|
recovery admission require the exact current profile and digest.
|
|
|
|
## Adding a provider
|
|
|
|
1. Add its profile at version 1 to `acpx-profiles.json`.
|
|
2. Add its attestation to `ACPX_RELEASE_ATTESTATIONS`, with its pins in
|
|
erasable TypeScript or JSON that the generator can read.
|
|
3. Run `generate:acpx-profiles`, then `node --test scripts/acpx-release-attestations.test.mjs`.
|
|
|
|
## Review criteria
|
|
|
|
- Does every provider digest change only with its upstream release?
|
|
- Is a new runner/pack contract declared where the `contractDigest` reads it?
|
|
- Does a version bump state which saved sessions it resets, and why?
|
|
- Does the change treat every ACPX harness the same way?
|
|
|
|
The canonical reference is
|
|
[`packages/paperclip-runner/docs/durable-recovery.md`](../packages/paperclip-runner/docs/durable-recovery.md#profile-identity).
|
|
The pack contract digest is described in
|
|
[`docker/daytona-runner/README.md`](../docker/daytona-runner/README.md).
|