* fix(annotate): add /api/save-notes POST endpoint to both servers
Copies the save-notes route from the plan review server into the
annotate server (Bun source and Pi extension copy), enabling Save to
Obsidian in annotation mode.
Fixes#844
* test(annotate): add saveToObsidian unit tests and HTTP endpoint tests
Verifies saveToObsidian writes files correctly and handles missing
vaults. HTTP endpoint tests cover success, empty integrations, and
integration-level error (not 500).
Imports consolidation from ./integrations into a single statement.
* fix(annotate): normalize server port fallback
* refactor(server): extract shared handleSaveNotes handler, fix catch-block bug
Move the /api/save-notes logic into shared handler modules
(shared-handlers.ts for Bun, handlers.ts for Pi) following the existing
pattern for handleImage, handleUpload, handleDraftSave. Replaces four
inline copies with two canonical implementations.
Fixes:
- Bun annotate catch block now correctly returns 500 (was logging only)
- Misindented brace in Pi serverAnnotate.ts resolved by extraction
- Revert unrelated port fallback change (keep server.port! for consistency)
- Static imports in integrations.test.ts
- Add /api/save-notes to CLAUDE.md Annotate Server API table
* fix(opencode): inject annotate server starter instead of global mock.module
commands.test.ts mocked the annotate server with
`mock.module("@plannotator/server/annotate", ...)`. Bun module mocks are
process-global and cannot be unset (oven-sh/bun#7823, #12823), so the stub
leaked into every suite that runs after it — in particular any test that boots
the real annotate server received a stub with no `.url`.
Make `startAnnotateServer` injectable through the existing CommandDeps
(defaulting to the real import, so production is unchanged) and have the test
pass its stub that way. This keeps the fake local to the opencode suite and
unblocks real annotate-server integration tests.
* test(server): cover save-notes — handler unit tests + annotate e2e wiring
- shared-handlers.test.ts: unit-test handleSaveNotes directly (Obsidian write,
empty integrations, integration-error reported not thrown, 500 on bad body).
- annotate.test.ts: boot the real annotate server and POST /api/save-notes,
asserting it is served as JSON (not the SPA HTML catch-all) — the regression
guard for #844. Now possible because the opencode suite no longer installs a
global annotate module mock.
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>