Add bookmark agent API (PUT/DELETE /docs/{slug}/bookmark, GET /bookmarks)#14
Merged
Conversation
…rks)
The web bookmarks feature shipped without its agent-facing API: no
/api/v1 handlers, no OpenAPI spec coverage, and no /llms.txt or SKILL.md
docs. Add the three handlers on top of the existing bookmark store/view
layer, register them in the code-first OpenAPI spec, and document them
in the single-source skill content.
- PUT /api/v1/docs/{slug}/bookmark — idempotent save; requires view access
- DELETE /api/v1/docs/{slug}/bookmark — idempotent remove; works on a
revoked/deleted doc (removeBookmarkBySlug resolves the id past soft-delete)
- GET /api/v1/bookmarks?scope=owned|shared|all — access re-resolved per read
(owner|editor|commenter|viewer|public|link|revoked)
Access re-resolution is factored into a pure resolveBookmarkView helper
with unit tests. Regenerated spec artifacts + SKILL.md; spec:check green
(28 endpoints, 21 paths).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tnsardesai
marked this pull request as ready for review
July 24, 2026 23:53
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 082e79f. Configure here.
GET /api/v1/bookmarks applied the SQL LIMIT across the full bookmark set and then filtered owned/shared in memory, so a scoped request could return fewer than limit rows (or none) while more matching bookmarks existed. Push the scope filter into listBookmarks' query so LIMIT bounds the requested scope. Verified against a local Postgres, including the scope=owned&limit=2 regression. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

The web bookmarks feature shipped its human UI, backend store/view layer, and migration — but not the agent-facing API described alongside it. There are no
/api/v1bookmark handlers, so the endpoints 404, and because no handler exists on diskspec:check’s cross-surface guard had nothing to flag — the surface was left undocumented in the OpenAPI spec,/llms.txt, andSKILL.md.This PR adds the three endpoints on top of the bookmark store/view layer that already landed.
Endpoints
PUT /api/v1/docs/{slug}/bookmark— idempotent save. Requires view access (owner / grant / public /?viewtoken=); an inaccessible doc404s (no existence oracle). Persists the view token only when it is the actual basis for access, mirroring the web bookmark POST. Keyed by the key’s email, so it unifies with the account’s signed-in web bookmarks.DELETE /api/v1/docs/{slug}/bookmark— idempotent remove; succeeds when nothing was bookmarked and still drops a bookmark for a revoked/deleted doc (removeBookmarkBySlugresolves the id pastfindBySlug’s soft-delete filter).GET /api/v1/bookmarks?scope=owned|shared|all— newest first, each item’saccessre-resolved at read time (owner|editor|commenter|viewer|public|link|revoked); a withdrawn doc readsrevokedwith a nullurland the bookmark-time title snapshot. Defaults toscope=all. Scope is filtered in SQL solimitbounds the requested scope (no underfill).Scope:
docs.readfor all three — bookmarking needs only to read the doc and writes the caller’s own personal state, not the document.Also
resolveBookmarkViewhelper (no DB/session) with unit tests, matching the existing pure-view test pattern.lib/skill-content.ts→/llms.txt+SKILL.md).lib/openapi/generated.{yaml,json},generated-spec.ts, andSKILL.md.Verification
npm run spec:check— green (28 endpoints, 21 paths; spec ↔ on-disk routes ↔/llms.txtall agree).npx vitest run— 141 passed (135 prior + 6 newresolveBookmarkViewcases).npx tsc --noEmit— clean.postgres:16→npm run migrate→dev-seed): exercised the DB paths end-to-end —saveBookmark(idempotency + view-token COALESCE persistence),listBookmarksscope/limit for owned/shared/all, andremoveBookmarkBySlugon a soft-deleted doc. 11/11 checks passed, including thescope=owned&limit=2regression below.next lintis not configured in a fresh checkout (prompts for interactive setup), so it was not run; the typecheck + spec:check + tests + local DB run are the gates that did.Review follow-up
Cursor Bugbot + Vercel VADE both flagged that
GET /api/v1/bookmarksappliedLIMITbefore the in-memory scope filter, so a scoped request could underfill. Fixed in5fbd2d1by pushing the scope filter into thelistBookmarksSQL; covered by the local Postgres run.🤖 Generated with Claude Code