docs/plans/2026-03-13-issue-documents-plan.md
Status: Draft
Owner: Backend + UI + Agent Protocol
Date: 2026-03-13
Primary issue: PAP-448
Add first-class documents to Paperclip as editable, revisioned, company-scoped text artifacts that can be linked to issues.
The first required convention is a document with key plan.
This solves the immediate workflow problem in PAP-448:
<plan> blocksGET /api/issues/:id should include the full plan document and expose the other available documentsThis should be built as the text-document slice of the broader artifact system, not as a replacement for attachments/assets.
assets + issue_attachments).Recommendation:
This keeps PAP-448 focused while still fitting the larger artifact direction.
plan.plan document automatically available in the normal issue fetch used by agents/heartbeats.<plan>-in-description convention in skills/docs.Each issue can have multiple documents. Each document relation has a stable key:
plandesignnotesreportKey rules:
The plan key is conventional and reserved by Paperclip workflow/docs.
V1 documents should be text-first, not arbitrary blobs.
Recommended supported formats:
markdownplain_textjsonhtmlRecommendation:
markdownEvery document update creates a new immutable revision.
The current document row stores the latest snapshot for fast reads.
Do not use silent last-write-wins.
Updates should include baseRevisionId:
baseRevisionId must match current latest revision409 ConflictThis is important because both board users and agents may edit the same document.
GET /api/issues/:id should include:
planDocument when a plan document existsdocumentSummaries for all linked documentsIt should not inline every document body by default.
This keeps issue fetches useful for agents without making every issue payload unbounded.
<plan> compatibilityIf an issue has no plan document but its description contains a legacy <plan> block:
plan document when both existRecommendation:
Recommendation: make documents first-class, but keep issue linkage explicit via a join table.
This preserves foreign keys today and gives a clean path to future project_documents or company_documents tables later.
documentsCanonical text document record.
Suggested columns:
idcompany_idtitleformatlatest_bodylatest_revision_idlatest_revision_numbercreated_by_agent_idcreated_by_user_idupdated_by_agent_idupdated_by_user_idcreated_atupdated_atdocument_revisionsAppend-only history.
Suggested columns:
idcompany_iddocument_idrevision_numberbodychange_summarycreated_by_agent_idcreated_by_user_idcreated_atConstraints:
(document_id, revision_number)issue_documentsIssue relation + workflow key.
Suggested columns:
idcompany_idissue_iddocument_idkeycreated_atupdated_atConstraints:
(company_id, issue_id, key)(document_id) to keep one issue relation per document in v1assets for this?Because assets solves blob storage, not:
planGET /issues/:idDocuments and attachments should remain separate primitives, then meet later in a deliverables/artifact read-model.
Add:
DocumentFormatIssueDocumentIssueDocumentSummaryDocumentRevisionRecommended IssueDocument shape:
type DocumentFormat = "markdown" | "plain_text" | "json" | "html";
interface IssueDocument {
id: string;
companyId: string;
issueId: string;
key: string;
title: string | null;
format: DocumentFormat;
body: string;
latestRevisionId: string;
latestRevisionNumber: number;
createdByAgentId: string | null;
createdByUserId: string | null;
updatedByAgentId: string | null;
updatedByUserId: string | null;
createdAt: Date;
updatedAt: Date;
}
Recommended IssueDocumentSummary shape:
interface IssueDocumentSummary {
id: string;
key: string;
title: string | null;
format: DocumentFormat;
latestRevisionId: string;
latestRevisionNumber: number;
updatedAt: Date;
}
Extend Issue with:
interface Issue {
...
planDocument?: IssueDocument | null;
documentSummaries?: IssueDocumentSummary[];
legacyPlanDocument?: {
key: "plan";
body: string;
source: "issue_description";
} | null;
}
This directly satisfies the PAP-448 requirement for heartbeat/API issue fetches.
Recommended endpoints:
GET /api/issues/:issueId/documentsGET /api/issues/:issueId/documents/:keyPUT /api/issues/:issueId/documents/:keyGET /api/issues/:issueId/documents/:key/revisionsDELETE /api/issues/:issueId/documents/:key optionally board-only in v1Recommended PUT body:
{
title?: string | null;
format: "markdown" | "plain_text" | "json" | "html";
body: string;
changeSummary?: string | null;
baseRevisionId?: string | null;
}
Behavior:
baseRevisionId: createbaseRevisionId: updatebaseRevisionId: 409Recommended delete rule for v1:
That keeps automated systems from removing canonical docs too easily.
Add a new Documents section directly under the issue description.
Recommended behavior:
plan first when presentRecommended presentation order:
This matches the request that documents live under the description while still leaving attachments available.
Recommendation:
409If there is no stored plan document but legacy <plan> exists:
Legacy plan from descriptionUpdate the Paperclip agent workflow so planning no longer edits the issue description.
Required changes:
skills/paperclip/SKILL.md<plan> instructions with document creation/update instructionsdocs/api/issues.md<plan> blocksNew rule:
planThis work should explicitly feed the broader artifact/deliverables direction.
Recommendation:
document to any future ArtifactKindThe artifact proposal currently has no explicit document kind. It should.
Recommended future shape:
type ArtifactKind =
| "document"
| "attachment"
| "workspace_file"
| "preview"
| "report_link";
Files:
packages/db/src/schema/documents.tspackages/db/src/schema/document_revisions.tspackages/db/src/schema/issue_documents.tspackages/db/src/schema/index.tspackages/db/src/migrations/*packages/shared/src/types/issue.tspackages/shared/src/validators/issue.ts or new document validator filepackages/shared/src/index.tsAcceptance:
Files:
server/src/services/issues.ts or server/src/services/documents.tsserver/src/routes/issues.tsserver/src/services/activity.ts callsitesBehavior:
GET /issues/:id returns planDocument + documentSummariesAcceptance:
409Files:
ui/src/api/issues.tsui/src/lib/queryKeys.tsui/src/pages/IssueDetail.tsxBehavior:
Acceptance:
plan doc from issue detail<plan>Files:
skills/paperclip/SKILL.mddocs/api/issues.mddoc/SPEC-implementation.md<plan>Acceptance:
Behavior:
<plan> blocks as fallbackFollow-up, not required for first merge:
baseRevisionId conflict handlingplanDocument and document summaries<plan> fallback behaviorRun before implementation is declared complete:
pnpm -r typecheck
pnpm test:run
pnpm build
Should v1 documents be markdown-only, with json/html/plain_text deferred?
Recommendation: allow all four in API, optimize UI for markdown only.
Should agents be allowed to create arbitrary keys, or only conventional keys?
Recommendation: allow arbitrary keys with normalized validation; reserve plan as special behavior only.
Should delete exist in v1? Recommendation: yes, but board-only.
Should legacy <plan> blocks ever be auto-migrated?
Recommendation: no automatic mutation in the first rollout.
Should documents appear inside a future Deliverables section or remain a top-level Issue section? Recommendation: keep a dedicated Documents section now; later also expose them in Deliverables if an aggregated artifact view is added.
Ship issue documents as a focused, text-first primitive now.
Do not try to solve full artifact unification in the same implementation.
Use:
planDocument embedded in normal issue fetches<plan> fallbackThis addresses the real planning workflow problem immediately and leaves the artifact system room to grow cleanly afterward.