Skip to content

Fix the docs outage, and let MCP actually file a document - #241

Merged
imshashank merged 16 commits into
mainfrom
claude/orbit-docs-mcp-gaps-128f3b
Aug 8, 2026
Merged

imshashank merged 16 commits into
mainfrom
claude/orbit-docs-mcp-gaps-128f3b

Conversation

@imshashank

@imshashank imshashank commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Why

/docs was answering 500 in production. The cause was schema drift, not code: the deployed build queries doc.sort_order, and the production database does not have that column.

GET /docs   error  column "sort_order" does not exist   (42703)
digest: '3028487799'

The docs tree change (#165) added doc.sort_order, doc.search_vector, doc_collection.position and doc_access_request, and shipped doc-tree-order-catchup.sql to seed the new order. That script only backfills values, it assumes the columns exist. The DDL half was never written, so it was never applied. Every other schema commit in the last three weeks has a matching catchup script; this one was the gap.

It was not only the docs page. In three hours of production logs: 29 failures on GET /api/sync, so realtime catch-up was down for the whole workspace.

Live drift, read from production:

missing table   doc_access_request
missing column  doc.search_vector
missing column  doc.sort_order
missing column  doc_collection.position

What this changes

The outage. packages/db/catchup/doc-tree-schema-catchup.sql adds the missing DDL, idempotent and in one transaction like the other catchup scripts. Verified against a database rebuilt from baseline 0000: reproduced the failure, applied the script, both failing production queries then succeeded, and a second run was clean.

So it cannot happen quietly again. bun run db:check-drift compares every table the Drizzle schema declares against information_schema and exits non zero when the database is behind, so CI can refuse to ship code the target database cannot answer. It also lists tables the schema no longer declares, because those are what db:push would drop, and push is not safe here: it runs drizzle-kit push --force, and the four standup* tables are no longer in the schema but still hold 31 rows in production. bun run db:catchup <file> applies one catchup file over the configured connection.

Filing a document from MCP. Twelve doc tools existed and none could put a document into a collection. Passing collection was accepted and dropped, which is how 148 documents were imported while every folder stayed empty, with no error anywhere.

  • create_doc and update_doc take collection; update_doc takes parent
  • list_docs filters by collection and by unfiled
  • list_doc_collections carries docCount and position
  • new: update_doc_collection, delete_doc_collection (with reassignTo), move_doc, unarchive_doc, delete_doc

A collection resolves by id, then by name, and an ambiguous name is a clear error rather than a guess.

One home per document. Orbit's rule is that a doc lives in a folder or in a project, never both, and the sidebar groups purely on projectId. Moving already refused the combination; creating and updating quietly allowed it. That is why filing alone would not have been enough: the imported documents carry a project, so they would have stayed under "Project docs" with the folders still empty. Filing a document now detaches it from its project, attaching it to a project unfiles it, and naming both in one call is refused. Production currently holds 7 documents in that invalid state.

Unknown arguments error. defineTool builds a strict object, so an unrecognised argument is a validation error on all 71 tools rather than a silent no-op. This is the bug that made the migration fail silently.

The docs UI. A doc opened in rich text with a full formatting bar pinned above it, under a header, under a row of pills: four bands of chrome between the breadcrumb and the first line, with the prose held to 45rem however wide the window was.

  • opens in markdown, which is what everything is written in
  • the formatting bar starts hidden, behind a toggle next to the mode switch
  • the pill row above the title is gone from the editing surface; the breadcrumb already names the folder and project
  • tighter title, reader padding and metadata spacing
  • reading width is a choice, defaulting to 68rem, with Comfortable / Wide / Full width in the doc menu

Mode, toolbar and width stick per person in local storage.

Large documents. A version snapshot compared the previous body in the app, so every autosave of a large document dragged the last saved copy out of Postgres just to decide whether anything had changed. It compares in SQL now. The limit is unchanged at 500,000 characters; the generated search vector is bounded at 200,000 and measures 165KB against Postgres's 1MB cap even for pathological content, so it was never the constraint.

Verification

  • bun run lint, check-comments, check-bytes, check-bun-imports, check-deps, typecheck: clean
  • packages/mcp-server: 160 pass, including the GitHub tools merged from main under the new strict arguments
  • apps/web: 1585 pass
  • packages/core: 725 pass on a clean lane (two timed out on a shared lane under contention with another agent's run, and pass in isolation)
  • New: 19 MCP doc tool tests covering every case in the spec, and 5 preference tests

Applying this

The catchup must be applied before this deploys, or the drift check will fail and /docs stays down:

bun --env-file=.env.production packages/db/src/apply-catchup.ts packages/db/catchup/doc-tree-schema-catchup.sql
bun --env-file=.env.production packages/db/src/apply-catchup.ts packages/db/catchup/doc-tree-order-catchup.sql
bun --env-file=.env.production packages/db/src/check-drift.ts

Deliberately not here

  • description on a collection, which the spec asks for: it needs another schema change, and schema changes shipping ahead of the database is exactly what broke production. Worth doing once the drift check is in CI.
  • Nested collections, bulk move, and a standalone reorder_docs; move_doc with after/before is already the reorder primitive.
  • Repairing the 7 documents that hold both a collection and a project. That is a data change and should be a deliberate call, not a side effect of this PR.

Review

Greptile, ordering uses unresolved destination home (P1) — real, and fixed in 6751f05. A move naming both a parent and a neighbour looked for that neighbour in the collection the arguments asked for, but a nested doc inherits its parent's home, so the sibling set was wrong and a valid move was refused. plannedPlacement now asks core where the doc will land through the same resolution the move runs. Regression test added; verified it fails without the fix.

Greptile, catchup violates comment policy (P1) — disagreed, with a reply on the thread. check-comments inspects source, not SQL, the CI check passes with the comments present, and every existing script in packages/db/catchup carries the same header.

Two issues found in my own review before the bots: list_docs claimed an ordering it did not use, and collection plus unfiled in one call answered with nothing instead of saying why.

Greptile Summary

The PR restores the missing document-tree database schema, adds drift and catchup tooling, expands MCP document filing and collection operations, and revises the docs editing and reading experience.

  • Adds idempotent catchup DDL and database drift detection.
  • Adds MCP collection resolution, filing, movement, archive, deletion, and ordering operations.
  • Enforces one-home document placement and strict tool arguments.
  • Adds persistent editor-mode, toolbar, and reading-width preferences.
  • Moves large-document snapshot comparison into SQL.

Confidence Score: 3/5

The PR is not yet safe to merge because document ordering can still be silently misapplied during concurrent moves and the catchup script remains out of compliance with the repository’s mounted source-comment rule.

The parent-home resolution fix addresses the original ordering mismatch, but anchor IDs are still computed before the mutation transaction and can disappear from the re-resolved sibling scope, causing the requested position to fall back to the end of the list. The new catchup SQL also retains comments prohibited by the applicable repository guide.

Files Needing Attention: packages/mcp-server/src/tools/docs.ts, packages/core/src/content/doc-service.ts, packages/db/catchup/doc-tree-schema-catchup.sql

Important Files Changed

Filename Overview
packages/mcp-server/src/tools/docs.ts Expands MCP document operations and fixes parent-home planning, but destination anchor selection remains outside the transactional move and can become stale.
packages/core/src/content/doc-service.ts Adds one-home placement enforcement, collection movement, ordering, deletion, and SQL-side snapshot comparison; transactional movement can currently fall back when passed a stale anchor.
packages/db/catchup/doc-tree-schema-catchup.sql Adds the missing document-tree DDL idempotently, while retaining comments prohibited by the mounted repository guide.
packages/db/src/check-drift.ts Adds schema drift reporting for missing and extra database objects.
apps/web/src/features/docs/doc-editor.tsx Makes Markdown the default editor, adds collapsible formatting controls, and integrates mode-aware outline navigation.
apps/web/src/features/docs/use-doc-preferences.ts Adds validated, local-storage-backed document display preferences with cross-tab synchronization.

Sequence Diagram

sequenceDiagram
  participant MCP as MCP move_doc
  participant Planner as plannedPlacement
  participant Siblings as landingAnchors
  participant Core as moveDoc transaction
  participant DB as PostgreSQL
  MCP->>Planner: Resolve destination home
  Planner->>DB: Read current document and parent
  MCP->>Siblings: Resolve before/after anchor IDs
  Siblings->>DB: Read destination siblings
  MCP->>Core: Placement and anchor IDs
  Core->>DB: Begin transaction
  Core->>DB: Re-read and resolve placement
  Core->>DB: Read anchor order in resolved sibling scope
  Core->>DB: Update document placement and sort order
Loading

Reviews (11): Last reviewed commit: "fix(docs): a fence closes only on a line..." | Re-trigger Greptile

The docs tree change added doc.sort_order, doc.search_vector,
doc_collection.position and doc_access_request, and shipped
doc-tree-order-catchup.sql to seed the new order. That script only
backfills values, so a database that never had the columns applied
answers every read of a doc with 42703 column "sort_order" does not
exist, which takes out /docs and /api/sync together.

Add the DDL half, idempotent and in one transaction like the other
catchup scripts, plus doc.slug for a database old enough to miss it.
Applying schema is a manual step someone has to remember, and the docs
tree change is what happens when it is forgotten: code shipped naming a
column the database did not have.

db:check-drift compares every table the Drizzle schema declares against
information_schema and exits non zero on anything missing, so CI can
refuse to ship code the target database cannot answer. It also lists the
tables the schema no longer declares, because those are exactly what
db:push would drop, and push is not safe against a database holding
data: it runs drizzle-kit push --force.

db:catchup applies one file from packages/db/catchup over the configured
connection, so bringing a database up to date is a command rather than a
paste into a SQL console.
A doc lives in a folder or in a project, never both. Moving already
refused the combination; creating and updating quietly allowed it, which
is how a doc ends up claiming a folder while the sidebar files it under
its project. Filing a doc now detaches it from its project and attaching
it to a project unfiles it, and naming both in one call is refused.

listDocCollections carries docCount, counting only the live docs the
caller may read, so a folder can be seen to be empty without listing
every doc. listDocs takes unfiled, so the docs in no folder are findable
rather than merely absent from every folder.

Deleting a folder takes an optional collection to hand its docs to, and
the docs are never deleted either way.

A version snapshot compared the previous body in the app, so every
autosave of a large doc dragged the last saved copy out of Postgres to
decide whether anything had changed. Compare it in SQL instead.
The doc tools could create a folder and list folders, and nothing else:
no tool could put a document into one. Passing collection to create_doc
or update_doc was accepted and dropped, so a migration could file 148
documents and leave every folder empty without one error.

create_doc and update_doc take collection, update_doc takes parent, and
both resolve a folder by id then by name, refusing an ambiguous name
rather than guessing. list_docs filters by collection and by unfiled.
list_doc_collections carries docCount and position. Adds
update_doc_collection, delete_doc_collection with reassignTo, move_doc,
unarchive_doc and delete_doc.

move_doc places a document against a named sibling the way dragging it
does, by reading the destination siblings and passing both anchors, so
after and before mean what they say rather than naming one edge.

Resolving a document now falls back to the archived ones, otherwise
unarchive_doc could never name the document it exists to restore.

defineTool builds a strict object, so an unrecognised argument is a
validation error on every tool rather than a silent no-op.
A doc opened in the rich text editor with a full formatting bar pinned
above it, under a header, under a row of pills, so four bands of chrome
sat between the breadcrumb and the first line of text, and the prose was
held to 45rem however wide the window was.

A doc now opens in markdown, which is what everything is written in, and
the formatting bar starts hidden behind a toggle beside the mode switch.
Both stick, per person, in local storage.

The pill row above the title is gone from the editing surface: the
breadcrumb in the header already says which folder and project a doc is
in. The title, the reader padding and the space under the metadata are
tighter, and the reader takes a width the reader chooses, defaulting to
68rem, with Comfortable, Wide and Full width in the doc menu.

Uploading moves into use-doc-uploads so the editor is about editing.
@imshashank
imshashank requested a review from pulkitxm as a code owner August 8, 2026 15:47
@vercel

vercel Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
orbit Ready Ready Preview Aug 8, 2026 5:21pm

Request Review

@github-actions github-actions Bot added tests Test coverage and test infrastructure area: web The Next.js app and its UI area: mcp The MCP server, its tools and its OAuth area: database Schema, migrations, queries, seed area: docs-feature The docs product and its editor dependencies Dependency updates labels Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds shared document preferences and upload handling, configurable reading widths, expanded document organization and lifecycle tools, stricter placement validation, an idempotent schema catch-up migration, and PostgreSQL drift-checking utilities.

Changes

Web document experience

Layer / File(s) Summary
Preference store and validation
apps/web/src/features/docs/use-doc-preferences.ts, apps/web/tests/features/docs/*
Adds validated, persisted, cross-tab-synchronized preferences for editor mode, toolbar visibility, and reading width.
Editor modes, uploads, and toolbar controls
apps/web/src/features/docs/doc-editor.tsx, apps/web/src/features/docs/use-doc-uploads.ts, apps/web/src/features/docs/editor/*
Extracts editor controls and upload handling. The editor uses shared preferences and reports sequential upload progress.
Reading width and editor surface
apps/web/src/features/docs/doc-header.tsx, apps/web/src/features/docs/doc-reader.tsx, apps/web/src/features/docs/doc-surface.tsx
Adds reading-width options and applies width-specific reader and editor layouts.
Outline navigation and surface tracking
apps/web/src/features/docs/outline.ts, apps/web/src/features/docs/use-editor-outline.ts, apps/web/src/features/docs/editor/markdown-code-editor.tsx, apps/web/e2e/doc-outline.spec.ts
Adds Markdown heading-line navigation and direct editor-surface tracking for both editor modes.

Document organization tools

Layer / File(s) Summary
Placement validation and service resolution
packages/shared/src/validators/doc.ts, packages/core/src/content/doc-service.ts
Enforces exclusive collection/project placement and adds placement resolution, reference lookup, unfiled filtering, collection counts, and optimized version comparison.
Document and collection reference tools
packages/mcp-server/src/resolve.ts, packages/mcp-server/src/tools/docs.ts, packages/mcp-server/src/tools/support.ts
Adds document and collection reference resolution, collection and unfiled filters, collection assignment, nullable relationship updates, and strict tool inputs.
Collection lifecycle and document moves
packages/core/src/content/doc-service.ts, packages/mcp-server/src/tools/docs.ts
Adds collection reassignment and deletion, sibling-aware document moves, archive restoration, and permanent document deletion.
Organization behavior validation
packages/mcp-server/tests/tools/docs.test.ts
Tests filing, hierarchy, collection lifecycle, ordering, archive and delete behavior, strict inputs, and filter validation.

Database operations

Layer / File(s) Summary
Document schema catch-up migration
packages/db/catchup/doc-tree-schema-catchup.sql
Adds document ordering and search fields, access-request records, notification reasons, foreign keys, uniqueness constraints, and indexes in an idempotent transaction.
Drift checking and catch-up execution
package.json, packages/db/package.json, packages/db/src/*, packages/db/tests/apply-catchup.test.ts
Adds scripts and CLIs for schema drift checks and catch-up execution. The tools inspect PostgreSQL metadata, format reports, validate SQL paths, execute SQL, and return status codes.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCP as MCP document tools
  participant Core as Core document service
  participant DB as PostgreSQL
  participant Sync as Sync publisher
  MCP->>Core: resolve document and planned placement
  Core->>DB: read placement and sibling state
  DB-->>Core: document and collection data
  Core->>DB: apply move or collection operation
  DB-->>Core: updated document actions
  Core->>Sync: publish resulting actions
  Sync-->>MCP: return operation result
Loading

Possibly related PRs

  • Noveum/orbit#23: Provides the attachment upload APIs used by useDocUploads.
  • Noveum/orbit#40: Modifies the same document editor, outline, rich-text editor, and document-service areas.
  • Noveum/orbit#61: Introduces the outline and scroll handling extended by this PR.

Suggested reviewers: pulkitxm

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the production docs outage fix and the main MCP document-filing change.
Description check ✅ Passed The description directly explains the outage, database changes, MCP tooling, UI updates, tests, deployment steps, and known review concerns.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/orbit-docs-mcp-gaps-128f3b

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…lter

The description promised newest first while the query orders by the
sidebar order, or by relevance once a query is given.

Asking for one collection and for the unfiled documents in the same call
answered with nothing, because no document is both. Say so instead.
Comment thread packages/mcp-server/src/tools/docs.ts Outdated
Comment thread packages/db/catchup/doc-tree-schema-catchup.sql
A move that named a parent and a neighbour looked for that neighbour in
the collection the arguments asked for, but a doc nested under a parent
inherits the parent's collection and project instead. The sibling set
was the wrong one, so a valid move was refused with a neighbour that
does not sit where the document is moving to.

Ask core where the doc will land, through the same resolution the move
itself runs, rather than deriving a second answer here that can drift
from it.
@imshashank

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/features/docs/editor/editor-toolbar.tsx (1)

152-205: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Hide all rich-text formatting controls when collapsed is true.

Line 152 hides only undo, redo, and text-style controls. Bold, italic, lists, links, and other formatting controls still render below it.

DocEditor passes toolbarCollapsed={!toolbar} and labels the action "Hide formatting". The rich editor therefore does not apply the saved toolbar preference. Gate the remaining formatting controls on !collapsed, while keeping the mode switch and upload status available.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/features/docs/editor/editor-toolbar.tsx` around lines 152 - 205,
Update the editor toolbar rendering so every rich-text formatting control,
including bold, italic, lists, links, and related controls, is gated by
!collapsed just like the existing undo, redo, and text-style block. Keep the
mode switch and upload status rendered when collapsed, and preserve the current
compact behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/features/docs/doc-editor.tsx`:
- Around line 137-140: Update the tab option className in the mode selector to
use the shared tab interaction helper instead of the raw transition-colors and
hover:text-muted classes, while preserving the existing active-state styling and
layout classes.

In `@apps/web/src/features/docs/doc-header.tsx`:
- Around line 120-139: Update ReadingWidthItems to use the existing Radix
dropdown radio-group and radio-group-item primitives for the mutually exclusive
reading widths. Bind the group value to width, update selection through
setWidth, and expose each option’s checked state while preserving the current
labels, test IDs, and styling.

In `@apps/web/src/features/docs/use-doc-preferences.ts`:
- Around line 79-90: Add store behavior tests alongside the existing
parsePreferences tests, covering setter persistence, storage-event updates to
subscribers, and resetDocPreferences refreshing the in-memory snapshot. Exercise
the public preference store APIs and verify persisted storage, listener
notifications, and updated snapshots so persistence and cross-tab
synchronization regressions are detected.
- Around line 27-40: Replace the manual validation and cast in parsePreferences
with a shared docPreferencesSchema exported from `@orbit/shared`, using safeParse
for localStorage input. Preserve DEFAULT_DOC_PREFERENCES for invalid top-level
data and retain per-field fallback behavior by merging parsed values with the
defaults when validation fails partially.

In `@packages/core/src/content/doc-service.ts`:
- Around line 1480-1507: Update deleteDocCollection and its emptyCollection call
to enforce document-level write authorization for every document being
reassigned or removed, using assertDocWritable or the existing equivalent before
applying changes. Preserve the collection-level doc:write check and ensure
unauthorized private documents cannot be moved through collection deletion.

In `@packages/db/catchup/doc-tree-schema-catchup.sql`:
- Around line 21-24: Move the `begin;` statement before the `ALTER TYPE
public.notification_reason ... ADD VALUE` statements so both enum additions
execute within the transaction. Keep the existing enum ordering and `if not
exists` behavior unchanged; document a non-atomic path only if pre-16 PostgreSQL
deployments must be supported.

In `@packages/db/src/check-drift.ts`:
- Around line 125-133: At packages/db/src/check-drift.ts:125-133, update the
import.meta.main CLI boundary to validate DATABASE_URL with the shared Zod
schema before calling liveColumns, preserving the existing missing-input exit
behavior. At packages/db/src/apply-catchup.ts:13-38, validate both the database
URL and catch-up script identifier with the shared schemas, then resolve the
validated identifier only to approved internal packages/db/catchup files before
readFile and sql.unsafe; do not accept arbitrary readable paths.
- Around line 26-60: Extend expectedTables and driftBetween to compare complete
schema metadata, not only table and column names. Include constraints, indexes,
defaults, generated expressions, and required enum values from the declarations
in content.ts, and obtain the corresponding live metadata for comparison. Report
missing or mismatched objects so catch-up migrations such as doc_access_request
and doc.search_vector are fully validated.

In `@packages/mcp-server/src/tools/docs.ts`:
- Around line 27-29: Move the shared MCP reference schemas represented by docRef
and collectionRef into `@orbit/shared`, export them there, and update the new tool
parameters in the server package to import and reuse those schemas instead of
defining local z.string validations. Preserve their current non-empty validation
and descriptions.
- Around line 79-84: Update packages/mcp-server/src/tools/docs.ts lines 79-84 in
resolveDoc to use an unbounded, lifecycle-aware exact resolver that matches
readable document IDs or titles; support live-only resolution for placement
operations and explicit archived resolution only for lifecycle operations.
Update packages/mcp-server/src/tools/docs.ts lines 143-150 to query destination
siblings directly in sort order rather than filtering a globally limited list,
ensuring archived documents are not used as parents or ordering neighbours.
- Around line 107-114: Update neighbourOf to reject calls where both after and
before are defined by raising the existing validationFailed error; preserve
resolving a single anchor and returning null when neither is provided.

In `@packages/mcp-server/tests/tools/docs.test.ts`:
- Around line 30-50: Update collectionId, makeDoc, listCollections, and listDocs
to validate TestClient.result payloads with the appropriate Zod response schemas
from `@orbit/shared` before accessing or returning their properties. Replace the
direct type assertions with schema parsing for each MCP tool response,
preserving the existing returned IDs, documents, and collections.
- Around line 188-194: Extend CollectionShape and the “renames and re-icons a
collection” test to include the icon field, then assert the listed collection’s
icon is “rocket” alongside the existing name assertion. Ensure the test fails if
the icon update is not persisted.

---

Outside diff comments:
In `@apps/web/src/features/docs/editor/editor-toolbar.tsx`:
- Around line 152-205: Update the editor toolbar rendering so every rich-text
formatting control, including bold, italic, lists, links, and related controls,
is gated by !collapsed just like the existing undo, redo, and text-style block.
Keep the mode switch and upload status rendered when collapsed, and preserve the
current compact behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 88143f6e-ed2a-42eb-abf3-87ed1b2a8f89

📥 Commits

Reviewing files that changed from the base of the PR and between 2da40e0 and 6751f05.

📒 Files selected for processing (22)
  • apps/web/src/features/docs/doc-editor.tsx
  • apps/web/src/features/docs/doc-header.tsx
  • apps/web/src/features/docs/doc-reader.tsx
  • apps/web/src/features/docs/doc-surface.tsx
  • apps/web/src/features/docs/editor/editor-toolbar.tsx
  • apps/web/src/features/docs/editor/rich-text-editor.tsx
  • apps/web/src/features/docs/use-doc-preferences.ts
  • apps/web/src/features/docs/use-doc-uploads.ts
  • apps/web/tests/features/docs/doc-anchor-editor.test.tsx
  • apps/web/tests/features/docs/doc-preferences.test.ts
  • apps/web/tests/features/docs/doc-surface-anchors.test.tsx
  • package.json
  • packages/core/src/content/doc-service.ts
  • packages/db/catchup/doc-tree-schema-catchup.sql
  • packages/db/package.json
  • packages/db/src/apply-catchup.ts
  • packages/db/src/check-drift.ts
  • packages/mcp-server/src/resolve.ts
  • packages/mcp-server/src/tools/docs.ts
  • packages/mcp-server/src/tools/support.ts
  • packages/mcp-server/tests/tools/docs.test.ts
  • packages/shared/src/validators/doc.ts

Comment thread apps/web/src/features/docs/doc-editor.tsx
Comment thread apps/web/src/features/docs/doc-header.tsx
Comment thread apps/web/src/features/docs/use-doc-preferences.ts
Comment thread apps/web/src/features/docs/use-doc-preferences.ts
Comment thread packages/core/src/content/doc-service.ts
Comment thread packages/mcp-server/src/tools/docs.ts
Comment thread packages/mcp-server/src/tools/docs.ts
Comment thread packages/mcp-server/src/tools/docs.ts
Comment thread packages/mcp-server/tests/tools/docs.test.ts
Comment thread packages/mcp-server/tests/tools/docs.test.ts
The outline was only rendered beside the rich editor, so defaulting to
markdown took the table of contents off every document, which the end to
end suite caught.

The outline is computed from the markdown either way, so render it in
both modes. In markdown it navigates by scrolling the source to the
heading's own line, through headingLineNumbers, which skips a hash
inside a fenced code block and counts a setext heading on the line its
text sits on, so the indexes line up one for one with the rendered
outline. Active heading tracking stays a rich mode affordance: it reads
the position of rendered headings, and markdown mode has none.

The spec now covers both modes, since the default it asserted changed.
Comment thread packages/mcp-server/src/tools/docs.ts
…ce the outline is on

Resolving a doc ref and finding a destination's siblings both read
listDocs, which stops at DOC_LIST_LIMIT. Past that window a document
could not be named and a neighbour could be missing from the anchors,
which would have surfaced as a move refused for no visible reason.
findDocsByRef and listDocSiblings ask for exactly what is wanted, with
no limit. Only lifecycle tools fall back to the archived ones, so an
archived doc can no longer be a parent or an ordering neighbour.

move_doc refuses a call naming both the sibling to land after and the
one to land before, rather than honouring one and dropping the other.

The outline held its scroll container in a ref, so it measured whatever
was mounted on its first run and never noticed a replacement: switching
editor mode swapped the container and active heading tracking stopped.
It takes the element itself now, so React re-runs the measurement when
the surface changes.

Also: the mode switch uses the shared tab interaction token rather than
hand rolled hover classes, apply-catchup resolves only files inside
packages/db/catchup, check-drift says out loud that it compares names
and not constraints, indexes or generated expressions, and the
preference store has tests for persistence and cross tab updates.
Comment thread apps/web/tests/features/docs/doc-preferences.test.ts Fixed
Comment thread apps/web/tests/features/docs/doc-preferences.test.ts Fixed
CodeQL reads StorageEvent through an extern that predates the init
dictionary, so passing one is reported as a superfluous argument. A
storage event with no key is what a browser sends when storage was
cleared wholesale, and the store treats it the same way: re-read
everything. Same path, no alert.
Comment thread apps/web/tests/features/docs/doc-preferences.test.ts Fixed
Comment thread apps/web/tests/features/docs/doc-preferences.test.ts Fixed
…nnot read

CodeQL bundles an extern where StorageEvent takes no arguments at all,
from before it was constructible, so every form of new StorageEvent is
reported. The comment policy allows no suppression directive for it.

Dispatch a plain Event carrying a null key instead, which is the shape
the listener actually reads and what a browser sends when storage was
cleared wholesale.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/features/docs/outline.ts`:
- Around line 144-152: Update the fence-closing logic around FENCE and marker in
apps/web/src/features/docs/outline.ts lines 144-152 to close only when the
candidate line contains the matching fence marker followed solely by permitted
trailing whitespace; marker text such as “not-a-closer” must remain code
content. Add a regression case in apps/web/tests/features/docs/outline.test.ts
lines 49-57 proving headings stay hidden after an invalid closer and become
visible only after a valid closer.

In `@apps/web/tests/features/docs/use-editor-outline.test.tsx`:
- Around line 127-134: Update the Swapping test around useEditorOutline to
verify the replacement surface is actually observed: render headings with
distinct offsets in both rich and markdown surfaces, dispatch a scroll event on
the markdown replacement container after rerendering, and assert that activeId
reflects the markdown surface’s heading. Replace the measuring-only assertion
with this behavior-focused check while preserving the existing surface
replacement assertions.

In `@packages/db/src/apply-catchup.ts`:
- Around line 2-14: Update the CATCHUP_DIR initialization used by catchupPath to
convert the directory URL with fileURLToPath(new URL('../catchup/',
import.meta.url)) instead of reading URL.pathname, and import fileURLToPath from
node:url. Preserve the existing path validation and resolution behavior.

In `@packages/db/tests/apply-catchup.test.ts`:
- Around line 4-37: Add execution-focused tests for applyCatchup alongside the
existing catchupPath and targetName tests. Cover successful SQL loading and
execution, rejected queries, and verify the PostgreSQL client’s sql.end cleanup
runs through the finally path; mock the client and assert
sql.unsafe(body).simple() is invoked with the loaded script.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8ebe3049-ee46-44dc-b585-874790c6dade

📥 Commits

Reviewing files that changed from the base of the PR and between 6751f05 and 07872c3.

📒 Files selected for processing (17)
  • apps/web/e2e/doc-outline.spec.ts
  • apps/web/src/features/docs/doc-editor.tsx
  • apps/web/src/features/docs/doc-surface.tsx
  • apps/web/src/features/docs/editor/markdown-code-editor.tsx
  • apps/web/src/features/docs/editor/rich-text-editor.tsx
  • apps/web/src/features/docs/outline.ts
  • apps/web/src/features/docs/use-editor-outline.ts
  • apps/web/tests/features/docs/doc-preferences.test.ts
  • apps/web/tests/features/docs/long-document-cost.test.tsx
  • apps/web/tests/features/docs/outline.test.ts
  • apps/web/tests/features/docs/use-editor-outline.test.tsx
  • packages/core/src/content/doc-service.ts
  • packages/db/src/apply-catchup.ts
  • packages/db/src/check-drift.ts
  • packages/db/tests/apply-catchup.test.ts
  • packages/mcp-server/src/tools/docs.ts
  • packages/mcp-server/tests/tools/docs.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/src/features/docs/editor/rich-text-editor.tsx
  • apps/web/src/features/docs/doc-surface.tsx
  • packages/db/src/check-drift.ts
  • packages/mcp-server/src/tools/docs.ts
  • apps/web/src/features/docs/doc-editor.tsx

Comment thread apps/web/src/features/docs/outline.ts Outdated
Comment thread apps/web/tests/features/docs/use-editor-outline.test.tsx
Comment thread packages/db/src/apply-catchup.ts
Comment thread packages/db/tests/apply-catchup.test.ts
headingLineNumbers closed a fenced block on any line starting with a
matching marker, so a line like ```not-a-closer inside a block ended it,
counted the headings that followed as real, and drifted the indexes away
from the rendered outline the list is displayed from. A closing fence
carries no info string: require the rest of the line to be blank.

The surface swap test asserted only that React handed over the new
element, so it passed whether or not the outline measured it. It now
places headings on both surfaces, scrolls the replacement, and asserts
the active heading comes from it.

applyCatchup had no test that ran it. It now applies the doc tree script
to a scratch database twice, checks sort_order, slug and a generated
search_vector arrive and survive the second run, indexes a doc body
through that vector, and refuses a path outside the catchup directory.
@imshashank
imshashank merged commit f7db206 into main Aug 8, 2026
12 checks passed
@imshashank
imshashank deleted the claude/orbit-docs-mcp-gaps-128f3b branch August 8, 2026 17:27

This branch was successfully deployed

1 active deployment
Preview — 75766cd8 Deployed Aug 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: database Schema, migrations, queries, seed area: docs-feature The docs product and its editor area: mcp The MCP server, its tools and its OAuth area: web The Next.js app and its UI dependencies Dependency updates tests Test coverage and test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants