feat: notion-like document management with novel.sh editor#1
feat: notion-like document management with novel.sh editor#1Utkarshbhimte wants to merge 4 commits intostagingfrom
Conversation
- New documents table with project association (drizzle migration 0028) - Many-to-many linking: documents <> goals, documents <> issues - Full CRUD API for documents with link management - Novel.sh rich text editor in UI - Document list page grouped by project - Document detail page with editor, project selector, goal/issue linkers - Linked documents shown on issue and goal detail pages - Sidebar navigation for documents - Auto-save with debounced PATCH Co-Authored-By: Paperclip <noreply@paperclip.ing>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0077160575
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| assertCompanyAccess(req, existing.companyId); | ||
| const link = await svc.linkGoal(id, req.body.goalId); |
There was a problem hiding this comment.
Verify goal and issue links stay inside the document company
This only authorizes against the document's companyId before inserting the relationship. If a scoped board user or agent submits a goalId from another company, the FK insert succeeds and GET /documents/:id will start returning that foreign ID back to readers of this document; the /documents/:id/issues handler below has the same gap. AGENTS.md explicitly says company boundaries must be enforced in routes/services, so these link endpoints need a same-company check on the target row, not just document access.
Useful? React with 👍 / 👎.
| const link = await svc.linkGoal(id, req.body.goalId); | ||
| res.status(201).json(link); |
There was a problem hiding this comment.
Record activity for document link and unlink mutations
All four goal/issue link handlers mutate join tables and return immediately, but unlike create/update/delete they never call logActivity. AGENTS.md says new endpoints should "write activity log entries for mutations", so attaching or detaching a document from a goal/issue currently leaves no audit trail for operators.
Useful? React with 👍 / 👎.
ui/src/pages/DocumentDetail.tsx
Outdated
| const linkIssue = useMutation({ | ||
| mutationFn: (issueId: string) => documentsApi.linkIssue(documentId!, issueId), | ||
| onSuccess: () => |
There was a problem hiding this comment.
Wire the issue-link mutation into the document detail UI
This mutation is defined but never invoked anywhere in the component. The linked-issues section only renders existing links and unlink buttons, so board users have no UI path to create a document→issue association; as shipped, the new LinkedDocuments block on issue detail can only surface links inserted through manual API calls.
Useful? React with 👍 / 👎.
- Documents now require an issueId (not null) — every doc is created from a task - Document creation auto-links the source issue via document_issues - Doc detail page has issue sidebar showing: - Issue title, identifier, status, priority, assignee - Issue description (truncated) - Live comments feed (polls every 15s) - Documents list page shows issue picker modal when creating new doc - Agents can edit docs by commenting on the source issue - Updated migration, schema, types, validators, service, and UI Co-Authored-By: Paperclip <noreply@paperclip.ing>
Covers data model, API endpoints, UI pages, agent workflow, and editor details. Co-Authored-By: Paperclip <noreply@paperclip.ing>
- New endpoint: GET /api/issues/:issueId/document — agents can find their doc from their assigned task without knowing the doc ID - Updated docs with agent direct editing workflow (read → edit → save) - Agents can PATCH /api/documents/:id with content directly - Comment-based collaboration remains as secondary workflow Co-Authored-By: Paperclip <noreply@paperclip.ing>
**#1 — Missing `description` field in fields table** The create body example included `description` and the schema confirms `description: z.string().optional().nullable()`, but the reference table omitted it. Added as an optional field. **#2 — Concurrency policy descriptions were inaccurate** Original docs described both `coalesce_if_active` and `skip_if_active` as variants of "skip", which was wrong. Source-verified against `server/src/services/routines.ts` (dispatchRoutineRun, line 568): const status = concurrencyPolicy === "skip_if_active" ? "skipped" : "coalesced"; Both policies write identical DB state (same linkedIssueId and coalescedIntoRunId); the only difference is the run status value. Descriptions now reflect this: both finalise the incoming run immediately and link it to the active run — no new issue is created in either case. Note: the reviewer's suggestion that `coalesce_if_active` "extends or notifies" the active run was also not supported by the code; corrected accordingly. **paperclipai#3 — `triggerId` undocumented in Manual Run** `runRoutineSchema` accepts `triggerId` and the service genuinely uses it (routines.ts:1029–1034): fetches the trigger, enforces that it belongs to the routine (403) and is enabled (409), then passes it to dispatchRoutineRun which records the run against the trigger and updates its `lastFiredAt`. Added `triggerId` to the example body and documented all three behaviours. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What
Adds a full document management system to Paperclip — notion-style docs that link to projects, goals, and issues. Agents and users can collaborate on docs.
Changes (27 files, +2120 lines)
Database (drizzle)
documentstable — title, content (jsonb/tiptap format), projectId, created by agent/userdocument_goals— many-to-many linking docs to goalsdocument_issues— many-to-many linking docs to issues0028_documents.sqlAPI (
server/src/routes/documents.ts)GET /api/companies/:id/documents— list with filters (?projectId, ?goalId, ?issueId)POST /api/companies/:id/documents— createGET /api/documents/:id— read with linked goals/issuesPATCH /api/documents/:id— update (auto-save from UI)DELETE /api/documents/:id— deletePOST/DELETE /api/documents/:id/goals/:goalId— link/unlink goalsPOST/DELETE /api/documents/:id/issues/:issueId— link/unlink issuesUI
Shared types & validators
How to test