14 KiB
Issue Documents Plan
Status: Draft
Owner: Backend + UI + Agent Protocol
Date: 2026-03-13
Primary issue: PAP-448
Summary
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:
- plans should stop living inside issue descriptions as
<plan>blocks - agents and board users should be able to create/update issue documents directly
GET /api/issues/:idshould include the fullplandocument and expose the other available documents- issue detail should render documents under the description
This should be built as the text-document slice of the broader artifact system, not as a replacement for attachments/assets.
Recommended Product Shape
Documents vs attachments vs artifacts
- Documents: editable text content with stable keys and revision history.
- Attachments: uploaded/generated opaque files backed by storage (
assets+issue_attachments). - Artifacts: later umbrella/read-model that can unify documents, attachments, previews, and workspace files.
Recommendation:
- implement issue documents now
- keep existing attachments as-is
- defer full artifact unification until there is a second real consumer beyond issue documents + attachments
This keeps PAP-448 focused while still fitting the larger artifact direction.
Goals
- Give issues first-class keyed documents, starting with
plan. - Make documents editable by board users and same-company agents with issue access.
- Preserve change history with append-only revisions.
- Make the
plandocument automatically available in the normal issue fetch used by agents/heartbeats. - Replace the current
<plan>-in-description convention in skills/docs. - Keep the design compatible with a future artifact/deliverables layer.
Non-Goals
- full collaborative doc editing
- binary-file version history
- browser IDE or workspace editor
- full artifact-system implementation in the same change
- generalized polymorphic relations for every entity type on day one
Product Decisions
1. Keyed issue documents
Each issue can have multiple documents. Each document relation has a stable key:
plandesignnotesreport- custom keys later
Key rules:
- unique per issue, case-insensitive
- normalized to lowercase slug form
- machine-oriented and stable
- title is separate and user-facing
The plan key is conventional and reserved by Paperclip workflow/docs.
2. Text-first v1
V1 documents should be text-first, not arbitrary blobs.
Recommended supported formats:
markdownplain_textjsonhtml
Recommendation:
- optimize UI for
markdown - allow raw editing for the others
- keep PDFs/images/CSVs/etc as attachments/artifacts, not editable documents
3. Revision model
Every document update creates a new immutable revision.
The current document row stores the latest snapshot for fast reads.
4. Concurrency model
Do not use silent last-write-wins.
Updates should include baseRevisionId:
- create: no base revision required
- update:
baseRevisionIdmust match current latest revision - mismatch: return
409 Conflict
This is important because both board users and agents may edit the same document.
5. Issue fetch behavior
GET /api/issues/:id should include:
- full
planDocumentwhen aplandocument exists documentSummariesfor all linked documents
It should not inline every document body by default.
This keeps issue fetches useful for agents without making every issue payload unbounded.
6. Legacy <plan> compatibility
If an issue has no plan document but its description contains a legacy <plan> block:
- expose that as a legacy read-only fallback in API/UI
- mark it as legacy/synthetic
- prefer a real
plandocument when both exist
Recommendation:
- do not auto-rewrite old issue descriptions in the first rollout
- provide an explicit import/migrate path later
Proposed Data Model
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.
Tables
documents
Canonical 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_at
document_revisions
Append-only history.
Suggested columns:
idcompany_iddocument_idrevision_numberbodychange_summarycreated_by_agent_idcreated_by_user_idcreated_at
Constraints:
- unique
(document_id, revision_number)
issue_documents
Issue relation + workflow key.
Suggested columns:
idcompany_idissue_iddocument_idkeycreated_atupdated_at
Constraints:
- unique
(company_id, issue_id, key) - unique
(document_id)to keep one issue relation per document in v1
Why not use assets for this?
Because assets solves blob storage, not:
- stable keyed semantics like
plan - inline text editing
- revision history
- optimistic concurrency
- cheap inclusion in
GET /issues/:id
Documents and attachments should remain separate primitives, then meet later in a deliverables/artifact read-model.
Shared Types and API Contract
New shared types
Add:
DocumentFormatIssueDocumentIssueDocumentSummaryDocumentRevision
Recommended 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;
}
Issue type enrichment
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.
API endpoints
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/:keyoptionally board-only in v1
Recommended PUT body:
{
title?: string | null;
format: "markdown" | "plain_text" | "json" | "html";
body: string;
changeSummary?: string | null;
baseRevisionId?: string | null;
}
Behavior:
- missing document + no
baseRevisionId: create - existing document + matching
baseRevisionId: update - existing document + stale
baseRevisionId:409
Authorization and invariants
- all document records are company-scoped
- issue relation must belong to same company
- board access follows existing issue access rules
- agent access follows existing same-company issue access rules
- every mutation writes activity log entries
Recommended delete rule for v1:
- board can delete documents
- agents can create/update, but not delete
That keeps automated systems from removing canonical docs too easily.
UI Plan
Issue detail
Add a new Documents section directly under the issue description.
Recommended behavior:
- show
planfirst when present - show other documents below it
- render a gist-like header:
- key
- title
- last updated metadata
- revision number
- support inline edit
- support create new document by key
- support revision history drawer or sheet
Recommended presentation order:
- Description
- Documents
- Attachments
- Comments / activity / sub-issues
This matches the request that documents live under the description while still leaving attachments available.
Editing UX
Recommendation:
- use markdown preview + raw edit toggle for markdown docs
- use raw textarea editor for non-markdown docs in v1
- show explicit save conflicts on
409 - show a clear empty state: "No documents yet"
Legacy plan rendering
If there is no stored plan document but legacy <plan> exists:
- show it in the Documents section
- mark it
Legacy plan from description - offer create/import in a later pass
Agent Protocol and Skills
Update the Paperclip agent workflow so planning no longer edits the issue description.
Required changes:
- update
skills/paperclip/SKILL.md - replace the
<plan>instructions with document creation/update instructions - document the new endpoints in
docs/api/issues.md - update any internal planning docs that still teach inline
<plan>blocks
New rule:
- when asked to make a plan for an issue, create or update the issue document with key
plan - leave a comment that the plan document was created/updated
- do not mark the issue done
Relationship to the Artifact Plan
This work should explicitly feed the broader artifact/deliverables direction.
Recommendation:
- keep documents as their own primitive in this change
- add
documentto any futureArtifactKind - later build a deliverables read-model that aggregates:
- issue documents
- issue attachments
- preview URLs
- workspace-file references
The artifact proposal currently has no explicit document kind. It should.
Recommended future shape:
type ArtifactKind =
| "document"
| "attachment"
| "workspace_file"
| "preview"
| "report_link";
Implementation Phases
Phase 1: Shared contract and schema
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.tsor new document validator filepackages/shared/src/index.ts
Acceptance:
- schema enforces one key per issue
- revisions are append-only
- shared types expose plan/document fields on issue fetch
Phase 2: Server services and routes
Files:
server/src/services/issues.tsorserver/src/services/documents.tsserver/src/routes/issues.tsserver/src/services/activity.tscallsites
Behavior:
- list/get/upsert/delete documents
- revision listing
GET /issues/:idreturnsplanDocument+documentSummaries- company boundary checks match issue routes
Acceptance:
- agents and board can fetch/update same-company issue documents
- stale edits return
409 - activity timeline shows document changes
Phase 3: UI issue documents surface
Files:
ui/src/api/issues.tsui/src/lib/queryKeys.tsui/src/pages/IssueDetail.tsx- new reusable document UI component if needed
Behavior:
- render plan + documents under description
- create/update by key
- open revision history
- show conflicts/errors clearly
Acceptance:
- board can create a
plandoc from issue detail - updated plan appears immediately
- issue detail no longer depends on description-embedded
<plan>
Phase 4: Skills/docs migration
Files:
skills/paperclip/SKILL.mddocs/api/issues.mddoc/SPEC-implementation.md- relevant plan/docs that mention
<plan>
Acceptance:
- planning guidance references issue documents, not inline issue description tags
- API docs describe the new document endpoints and issue payload additions
Phase 5: Legacy compatibility and follow-up
Behavior:
- read legacy
<plan>blocks as fallback - optionally add explicit import/migration command later
Follow-up, not required for first merge:
- deliverables/artifact read-model
- project/company documents
- comment-linked documents
- diff view between revisions
Test Plan
Server
- document create/read/update/delete lifecycle
- revision numbering
baseRevisionIdconflict handling- company boundary enforcement
- agent vs board authorization
- issue fetch includes
planDocumentand document summaries - legacy
<plan>fallback behavior - activity log mutation coverage
UI
- issue detail shows plan document
- create/update flows invalidate queries correctly
- conflict and validation errors are surfaced
- legacy plan fallback renders correctly
Verification
Run before implementation is declared complete:
pnpm -r typecheck
pnpm test:run
pnpm build
Open Questions
-
Should v1 documents be markdown-only, with
json/html/plain_textdeferred? 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
planas 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.
Final Recommendation
Ship issue documents as a focused, text-first primitive now.
Do not try to solve full artifact unification in the same implementation.
Use:
- first-class document tables
- issue-level keyed linkage
- append-only revisions
planDocumentembedded in normal issue fetches- legacy
<plan>fallback - skill/docs migration away from description-embedded plans
This addresses the real planning workflow problem immediately and leaves the artifact system room to grow cleanly afterward.