Files
paperclip/skills-releases/paperclip/v0/references/cases.md
T
DottaandPaperclip c3bd0c5d50 feat(skills): add beta releases for the core Paperclip skill (#10228)
## Thinking Path

> - Paperclip is the open source control plane people use to organize
and operate AI-agent companies.
> - Agent behavior depends partly on the bundled Paperclip core skill
synchronized into each runtime.
> - The existing database and runtime plumbing already supports
immutable skill-version snapshots and per-agent version selections, but
no product workflow exposed that capability.
> - Replacing the live bundled skill globally would make champion
adoption risky and difficult to compare across agents.
> - This pull request adds an experimental, instance-level beta-skills
gate plus a repository release registry, immutable seeded releases,
enforcement, and a per-agent release picker.
> - The benefit is controlled per-agent evaluation of frozen core-skill
releases while the default-off path remains behaviorally unchanged.

## Linked Issues or Issue Description

### Subsystem affected

Cross-cutting: `server/`, `ui/`, `packages/db`, and `packages/shared`.

### Problem or motivation

Paperclip needs a safe way to evaluate improved versions of its core
operating skill without globally replacing the live default. Today the
version-snapshot and per-agent pin plumbing exists, but operators cannot
use it. A global replacement would make regressions difficult to contain
and would prevent controlled comparisons across agents.

### Proposed solution

Add a default-off instance experiment that exposes immutable, named
core-skill releases. When enabled, operators can pin each agent to a
seeded release; when disabled, every agent resolves the live default
while saved pins remain intact. Validate pinned writes at the API
boundary, gate reads at runtime, and expose the selection in the agent
Skills tab.

### Alternatives considered

- **Replace the bundled core skill globally:** rejected because it
changes every agent at once and provides no rollback/isolation boundary.
- **Ship releases as separate skills:** rejected because releases are
versions of one core capability, not independently enabled skills.
- **Store release snapshots only outside the repository:** rejected
because repository provenance and hashes make builds reproducible and
reviewable.

### Roadmap alignment

This extends the Skills Manager / Skill Studio direction in `ROADMAP.md`
by making core-skill versions operable per agent. It does not duplicate
another open implementation PR; GitHub searches found no related
`enableBetaSkills` change.

### Additional context

The feature remains experimental and default off. The V7 champion was
selected through a multi-model evaluation process, and the frozen
release contents are verified by SHA-256 below.

## What Changed

- Added the default-off instance-level `enableBetaSkills` experimental
flag.
- Added `skills-releases/paperclip/` with the ordered release registry
and frozen `v0` plus `v7-roster` snapshots.
- Added release metadata to `company_skill_versions` and idempotent
release seeding. The migration was planned as `0191`, then renumbered to
`0192` because current `master` claimed `0191` before final rebase.
- Added read-time gating and write-time validation so disabled instances
always resolve the live default and reject pinned-version writes.
- Added the per-agent Release picker in the agent Skills tab, including
responsive layout and beta-pin state.
- Kept `EDITS.md` out of the release registry and PR diff.

### V7 Adoption Evidence

- Paid roster: 6 models, 94-case suite.
- Result: 553/564 pass-within-2, mean 92.17/94, versus the P2 baseline
of 544/564.
- Reference model improved 84→91; maximin improved 84→90.
- Final report:
https://pages.paperclip.ing/skills/optimization/paperclip/pap-14624-p3-final-20260721/

### Provenance

- `v7-roster` is the Phase 1 champion plus additions-only edits
E107–E112. Per-edit rationale remains in the evals repository at
`source/v7-roster/EDITS.md` and is deliberately excluded from this PR.
- `v0` is the `skills/paperclip` tree from commit `ea66ea81`.
- Champion selection was accepted on July 21, 2026 via board card
`9c304fc2` (PAP-14624 G3).
- This delivery mechanism was accepted on July 24, 2026 via plan
revision `2367abd2` (PAP-14858).

### QA Evidence

- P4 QA matrix comment `b7f40522-4e9b-4a3a-9821-28e86fe1a987`: all 6
acceptance criteria passed.
- Automated QA matrix: 166 tests passed with 0 failures, including real
filesystem materialization and full SHA-256 assertions.
- UI QA exercised the real agent Skills tab at desktop and mobile widths
with the experimental flag both on and off.

## Verification

- `pnpm check:token-gates`
- Focused beta-release matrix: 169 tests passed across shared
validators, server services/routes/heartbeat behavior, instance settings
UI, and release picker UI.
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run`: server and UI partitions passed; one CLI doctor test
inherited temporary AWS credentials from the agent heartbeat and
expected no static credentials. The isolated rerun with
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`
unset passed 8/8.
- V7 SHA-256:
- `SKILL.md`:
`53ab290489684cbf116fdd1406a95f6b6f53c9c36358b1bf8bfeae481e253575`
- `references/cases.md`:
`3b821f59064a7761091020a14819a8d787131f24029748563d6c0e1be7e6eaec`
- `references/workflows.md`:
`69747bd6e05f7e3673d1e67b07ff295df1869c05e1fd029804d5fa9177db92cd`
- Confirmed 49 changed files, no `pnpm-lock.yaml`, no workflow changes,
and no `EDITS.md`.

## Risks

- **Migration:** low-to-moderate risk. Three nullable columns and one
partial unique index are added idempotently; existing rows remain valid.
- **Behavior:** low risk while the flag is off because read-time
resolution forces the live default and saved pins are preserved but
inactive.
- **Frozen content:** release snapshots intentionally diverge from
future live skill edits; provenance and hashes make that divergence
explicit and reproducible.
- **UI:** low risk. The picker only renders for the bundled core skill
when the experimental flag is enabled and seeded releases exist.

> This extends the existing Skills Manager / Skill Studio direction
described in `ROADMAP.md`; it does not duplicate another open
implementation PR. The GitHub PR search found no related
`enableBetaSkills` change.

## Model Used

- OpenAI Codex using `gpt-5.5` with reasoning and
terminal/code-execution tools; context-window size is not exposed by
this runtime. Earlier implementation commits also record Claude Opus 4.8
assistance where applicable.

## 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
- [ ] I have not referenced internal/instance-local Paperclip issues or
links (required governance identifiers are included above; no internal
URL is included)
- [ ] My branch name describes the change and contains no internal
Paperclip ticket id (the approved delivery plan mandated this shared
branch name)
- [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 <[email protected]>
2026-07-27 19:45:59 -05:00

7.5 KiB

Cases

Cases are agent-owned work records for durable outputs such as blog posts, research packets, release notes, incidents, QA runs, or generated asset sets. They are company-scoped and live beside issues: issues coordinate work, while cases preserve the structured object an agent is producing.

Cases are experimental and must be enabled with experimental.enableCases. If a route returns 403 Cases are disabled, stop and report that the operator must enable cases before the skill can use this surface.

Core Model

A case has:

  • identifier: server-assigned display id such as PAP-C42
  • caseType: skill-owned type such as blog_post, image_assets, or incident
  • key: optional deterministic upsert key inside (companyId, caseType)
  • title and optional summary
  • status: draft, in_progress, in_review, approved, done, or cancelled
  • fields: JSON object owned by the skill using the case
  • parentCaseId: optional parent case for child work
  • documents, attachments, issue links, labels, and events

Use deterministic caseType + key when a skill may be retried. Repeating POST /api/companies/:companyId/cases with the same caseType and key upserts the same case instead of creating a duplicate.

Upsert Semantics

POST /api/companies/:companyId/cases creates or upserts a case.

Request:

{
  "caseType": "blog_post",
  "key": "launch-announcement",
  "title": "Launch announcement",
  "summary": "Draft launch post for operators.",
  "status": "draft",
  "fields": {
    "slug": "launch-announcement",
    "target_audience": "operators"
  }
}

Response:

  • 201 when a new case was created
  • 200 when an existing (caseType, key) case was updated

Field behavior on upsert:

  • title is required and replaces the previous title.
  • projectId, summary, status, fields, and parentCaseId replace the previous value when present.
  • Omitted optional values preserve the previous value during upsert.
  • fields is replaced as a whole object when provided. It is not deep-merged. Send the complete desired JSON object each time.
  • Concurrent retries with the same (caseType, key) converge to one case.

Do not use a random key for retryable skills. Use a stable content slug, external id, source URL hash, or parent-derived request key.

Get a case by UUID or identifier:

GET /api/cases/PAP-C42

List cases for a company:

GET /api/companies/:companyId/cases?type=blog_post&status=active&q=launch

Useful filters:

  • type: exact caseType
  • status: exact lifecycle status, or active for non-terminal cases
  • projectId / project: project UUID
  • labelId / label: label UUID
  • q: identifier, title, summary, or key search
  • limit: 1-200, default 100

Documents

Use case documents for rich bodies such as drafts, briefs, reports, or plans.

PUT /api/cases/:caseIdOrIdentifier/documents/body
Content-Type: application/json

{
  "title": "Launch announcement body",
  "format": "markdown",
  "body": "# Launch announcement\n\nDraft copy...",
  "changeSummary": "Initial draft"
}

Updating an existing case document requires baseRevisionId:

{
  "baseRevisionId": "latest-revision-uuid",
  "body": "Updated body"
}

If you get 409 stale_base_revision, refetch the case detail, read the latest document revision id, merge intentionally, and retry with that baseRevisionId.

Fields

Each skill owns the schema of fields for the caseType it creates. Keep fields small, typed, and stable enough for other agents to inspect.

Examples:

{
  "slug": "launch-announcement",
  "target_audience": "operators",
  "publish_url": "https://example.com/blog/launch-announcement"
}

Patch fields or status with:

PATCH /api/cases/:caseIdOrIdentifier
Content-Type: application/json

{
  "status": "in_review",
  "fields": {
    "slug": "launch-announcement",
    "target_audience": "operators",
    "publish_url": "https://example.com/blog/launch-announcement"
  }
}

Remember: fields replaces the whole object when present.

Link cases to issues explicitly when needed:

POST /api/cases/:caseIdOrIdentifier/links
Content-Type: application/json

{
  "issueId": "issue-uuid",
  "role": "reference"
}

Roles:

  • origin: the issue/run that created the case
  • work: an issue/run that changed the case
  • reference: related issue context

Agent run writes auto-link the run's issue when Paperclip can resolve it from the run JWT or X-Paperclip-Run-Id. Creation/upsert writes use origin; later document, patch, and attachment writes use work when no link already exists. You do not need to manually link the current issue before writing the case.

Child Cases

Create child cases by setting parentCaseId to the parent case UUID.

{
  "caseType": "image_assets",
  "key": "launch-announcement:hero-images",
  "title": "Hero images for launch announcement",
  "parentCaseId": "parent-case-uuid",
  "fields": {
    "required_assets": ["hero", "social-card"]
  }
}

Use child cases when the output has independently inspectable pieces or when another agent can work on a bounded part without editing the parent case body.

Attachments

Attach generated files with multipart form data:

POST /api/cases/:caseIdOrIdentifier/attachments
Content-Type: multipart/form-data

[email protected]

The server records an asset and adds an attachment_added case event.

Lifecycle

Use the lifecycle consistently:

  • draft: case exists but useful work has not started
  • in_progress: an agent is actively producing or revising it
  • in_review: ready for reviewer, board, or downstream approval
  • approved: accepted but not finally shipped or archived
  • done: complete and no further action remains
  • cancelled: intentionally abandoned

Terminal statuses are done and cancelled; setting either records completedAt. Moving back to a non-terminal status clears completedAt.

Worked Blog Post Example

Create or upsert the parent blog post:

POST /api/companies/:companyId/cases
Content-Type: application/json

{
  "caseType": "blog_post",
  "key": "paperclip-cases-launch",
  "title": "Introducing Paperclip Cases",
  "summary": "Blog post explaining the cases surface for agent outputs.",
  "status": "in_progress",
  "fields": {
    "slug": "paperclip-cases-launch",
    "target_audience": "AI company operators",
    "publish_url": null
  }
}

Write the body:

PUT /api/cases/PAP-C42/documents/body
Content-Type: application/json

{
  "title": "Introducing Paperclip Cases",
  "format": "markdown",
  "body": "# Introducing Paperclip Cases\n\n..."
}

Create the child image-assets case:

POST /api/companies/:companyId/cases
Content-Type: application/json

{
  "caseType": "image_assets",
  "key": "paperclip-cases-launch:image-assets",
  "title": "Image assets for Introducing Paperclip Cases",
  "parentCaseId": "parent-case-uuid",
  "status": "in_progress",
  "fields": {
    "slug": "paperclip-cases-launch",
    "required_assets": ["hero", "social-card"],
    "publish_url": null
  }
}

Attach generated assets to the child, then patch both cases as they move through review:

PATCH /api/cases/PAP-C42
Content-Type: application/json

{
  "status": "in_review",
  "fields": {
    "slug": "paperclip-cases-launch",
    "target_audience": "AI company operators",
    "publish_url": "https://example.com/blog/paperclip-cases-launch"
  }
}

If the same skill retries the example with the same keys, it updates the parent and child cases rather than creating duplicates.