## 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]>
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 asPAP-C42caseType: skill-owned type such asblog_post,image_assets, orincidentkey: optional deterministic upsert key inside(companyId, caseType)titleand optionalsummarystatus:draft,in_progress,in_review,approved,done, orcancelledfields: JSON object owned by the skill using the caseparentCaseId: 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:
201when a new case was created200when an existing(caseType, key)case was updated
Field behavior on upsert:
titleis required and replaces the previous title.projectId,summary,status,fields, andparentCaseIdreplace the previous value when present.- Omitted optional values preserve the previous value during upsert.
fieldsis 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.
Read And Search
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: exactcaseTypestatus: exact lifecycle status, oractivefor non-terminal casesprojectId/project: project UUIDlabelId/label: label UUIDq: identifier, title, summary, or key searchlimit: 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.
Issue Links
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 casework: an issue/run that changed the casereference: 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 startedin_progress: an agent is actively producing or revising itin_review: ready for reviewer, board, or downstream approvalapproved: accepted but not finally shipped or archiveddone: complete and no further action remainscancelled: 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.