Skip to content

chore(release): promote 0.17.0 - #571

Merged
tomymaritano merged 13 commits into
mainfrom
develop
Aug 22, 2026
Merged

chore(release): promote 0.17.0#571
tomymaritano merged 13 commits into
mainfrom
develop

Conversation

@tomymaritano

Copy link
Copy Markdown
Collaborator

Workflow-only. Copies #570 onto main so workflow_dispatch (which reads this branch's YAML) has the same stamp + re-draft as develop (what workflow_run reads).

Does not cut a product release. Do not squash if more than this commit lands; this should be the #570 stamp/re-draft only.

Build & Publish #28 is already running on main for v0.16.1 — this PR is for the next dispatch, not that run.

…tach (#570)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Build & Publish **#27** ([run
32590544551](https://github.com/dripnex/app/actions/runs/32590544551))
built signed mac/win/linux installers, then the run went red.
[v0.16.1](https://github.com/dripnex/app/releases/tag/v0.16.1) notes
exist; assets were empty.

**Job that made #27 red:** `sync-develop` / Create sync PR
```
pull request create failed: GraphQL: GitHub Actions is not permitted to create or approve pull requests (createPullRequest)
```
Tomás: Settings → Actions → General → Workflow permissions → enable
**Allow GitHub Actions to create and approve pull requests**. That is
not why binaries are missing.

**Why assets were empty (all three `build` jobs + `publish`
succeeded):** `workflow_run` uses **develop**'s YAML (default branch),
which lacked the stamp step already on `main`. Tree version is still
`0.15.2` (tag-without-bump). electron-builder packaged
`Dripnex-0.15.2.*` and skipped upload onto the already-published
`v0.15.2`:
```
GitHub release not created  reason=existing type not compatible with publishing type tag=v0.15.2 version=0.15.2 existingType=release publishingType=draft
```
Signing/notary is fine (`notarization successful`; `CSC_*` / `APPLE_*`
present). Not a flake.

This PR (develop — the file `workflow_run` actually runs):

1. Stamp `package.json` from the tag before packaging (same step as
`main` / #540).
2. Re-draft the target GitHub Release so `--publish always` can attach
assets on retry.

No product code. AuthGate untouched. No store. No iOS.

### Retry (Tomás — cloud agent token cannot `workflow_dispatch`, HTTP
403)

v0.16.1 is already converted back to **draft** so electron-builder can
upload. Either:

```bash
# fastest: main already has the stamp step
gh workflow run "Build & Publish" --ref main -f tag=v0.16.1
```

```bash
# or this branch (stamp + auto re-draft)
gh workflow run "Build & Publish" --ref cursor/fix-v0161-build-assets-f239 -f tag=v0.16.1
```

Actions UI: Run workflow → branch `main` or
`cursor/fix-v0161-build-assets-f239` → tag `v0.16.1`.

`sync-develop` will fail again on the retry until the Actions PR setting
above is enabled. Installers still attach before that job.

## Type of Change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Related Issues

Unblocks empty v0.16.1 assets after promotion #569 / Build #27.

## Checklist

- [x] I've read CONTRIBUTING.md
- [x] Workflow-only change (no product tests)
- [x] PR targets `develop` branch (not `main`)

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-3629b701-130d-4cce-99f3-8e22cf2ff239?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-3629b701-130d-4cce-99f3-8e22cf2ff239&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@github-actions github-actions Bot added the ci label Aug 22, 2026
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@tomymaritano, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a50666f-a656-42f3-9067-30e273ae897d

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5481f and 2f2bb4a.

📒 Files selected for processing (47)
  • .github/workflows/automerge.yml
  • .github/workflows/build.yml
  • apps/desktop/package.json
  • apps/desktop/src/main/applyUserData.ts
  • apps/desktop/src/main/index.ts
  • apps/desktop/src/main/services/__tests__/aesKeyWrap.test.ts
  • apps/desktop/src/main/services/aesKeyWrap.ts
  • apps/desktop/src/main/services/encryptionService.ts
  • apps/desktop/src/renderer/components/NoteEditor.module.css
  • apps/desktop/src/renderer/components/NoteEditor.tsx
  • apps/desktop/src/renderer/components/NoteList.module.css
  • apps/desktop/src/renderer/components/NoteList.tsx
  • apps/desktop/src/renderer/components/auth/AuthGate.module.css
  • apps/desktop/src/renderer/components/auth/AuthGate.tsx
  • apps/desktop/src/renderer/components/auth/LoginBackdrop.module.css
  • apps/desktop/src/renderer/components/auth/LoginBackdrop.tsx
  • apps/desktop/src/renderer/components/editor/FormattingToolbar.module.css
  • apps/desktop/src/renderer/pages/settings/sections/McpCard.tsx
  • apps/desktop/src/renderer/themes/officialThemes.ts
  • apps/desktop/src/renderer/ui/tokens/tokens.css
  • apps/desktop/src/renderer/utils/__tests__/mcpSnippets.test.ts
  • apps/desktop/src/renderer/utils/mcpSnippets.ts
  • apps/desktop/src/renderer/utils/modAccel.ts
  • docs/RELEASE.md
  • docs/VISUAL_LANGUAGE.md
  • docs/adr/006-ci-green-automerge.md
  • docs/ai/README.md
  • docs/ai/mcp-plan.md
  • docs/ai/updates.md
  • docs/ci.md
  • docs/plugins/install-path.md
  • docs/themes/LOG.md
  • packages/mcp-server/README.md
  • packages/mcp-server/src/__tests__/httpClient.test.ts
  • packages/mcp-server/src/__tests__/httpConfig.test.ts
  • packages/mcp-server/src/__tests__/httpStore.test.ts
  • packages/mcp-server/src/__tests__/notes.test.ts
  • packages/mcp-server/src/__tests__/writes.test.ts
  • packages/mcp-server/src/db.ts
  • packages/mcp-server/src/http.ts
  • packages/mcp-server/src/httpStore.ts
  • packages/mcp-server/src/index.ts
  • packages/mcp-server/src/sqliteStore.ts
  • packages/mcp-server/src/store.ts
  • packages/storage-core/src/data/DataPaths.ts
  • packages/storage-core/src/data/index.ts
  • packages/storage-core/tests/data-paths.test.ts

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.

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Packaged Linux 0.16.1 still wrote plugins/DB/logs under
`~/.config/@dripnex/desktop` because Electron `app.getPath('userData')`
follows package.json `name` `@dripnex/desktop`. #567 only aligned CLI
`resolveUserDataRoot`. This PR sets the Electron app name and userData
to productName `Dripnex` before `createDataPaths`, so the packaged app
and `dripnex-plugin` share one folder.

## Type of Change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [x] Documentation update

## Related Issues

Closes #572

## What changed

1. **Runtime** — `applyProductUserData()` runs before Sentry and
`requestSingleInstanceLock`. It calls `app.setName('Dripnex')` and
`app.setPath('userData', resolveUserDataRoot())` unless Chromium already
has `--user-data-dir` (e2e).
2. **Package metadata** — root `productName: "Dripnex"` plus
electron-builder `extraMetadata.productName` so packaged `package.json`
does not rely on the scoped npm name for `app.getName()`.
3. **No silent migrate** — existing `@dripnex/desktop` with `dripnex.db`
or `plugins/` stays put via `pickUserDataRoot`. Fresh installs use
`~/.config/Dripnex` / `%APPDATA%/Dripnex` / `~/Library/Application
Support/Dripnex`.
4. **Docs** —
[`docs/plugins/install-path.md`](docs/plugins/install-path.md) records
the packaged-app contract.

No marketplace. No iOS. No release cut.

## Checklist

- [x] I've read [CONTRIBUTING.md](../CONTRIBUTING.md)
- [x] Targeted tests pass (`@dripnex/storage-core` 29,
`@dripnex/plugin-cli` 19) and desktop/storage-core typecheck
- [ ] Full `pnpm test` / `pnpm build` (not required for this path-only
change)
- [x] PR targets `develop` branch (not `main`)

## Test plan

- [x] Unit: `resolveElectronUserData` names the app `Dripnex`, defaults
to the packaged folder on Linux/Windows/macOS, keeps an existing scoped
folder that already has `dripnex.db`, honors `DRIPNEX_DATA_DIR`, leaves
`--user-data-dir` alone
- [ ] Packaged Linux log `dataDir` is `~/.config/Dripnex` on a fresh
machine
- [ ] CLI `dripnex-plugin` and the packaged app resolve the same plugins
dir
- [ ] e2e `--user-data-dir` / `DRIPNEX_DATA_DIR` still isolate tests

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-4d7bdc7f-21c2-43f5-9215-8ae46bc3b625?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-4d7bdc7f-21c2-43f5-9215-8ae46bc3b625&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Rebranded the desktop app as Dripnex.
* Improved shared data-folder handling between the desktop app and CLI.
* Added support for explicitly configured custom user-data directories.
  * Preserved existing application data across supported configurations.

* **Documentation**
  * Updated installation guidance for user-data directory resolution.

* **Tests**
* Added coverage for platform-specific paths, custom directories, and
application naming.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file app:desktop package:storage size/M and removed size/S labels Aug 22, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Adds three first-party desktop palettes on the existing
`ThemeDefinition` token contract. No stylesheet fork, no plugin
packages, no frosted window vibrancy.

- **Matcha** (`dripnex-matcha`) — light green-tea paper for calm reading
- **Phosphor** (`dripnex-phosphor`) — dark amber CRT / terminal glow
- **Fog** (`dripnex-fog`) — light coastal gray morning with a muted blue
accent

Author is Dripnex; `pluginId` is `dripnex`. Body text on `--bg-base` is
well above WCAG AA (≈10.8 / 13.2 / 10.6:1). `validateThemeTokens` still
copies `--accent` to `--accent-primary`.

Also creates `docs/themes/LOG.md` with today's three lines (2026-08-22,
America/Argentina/Buenos_Aires) so later daily theme runs skip these ids
and topics.

## Type of Change

- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [x] Documentation update

## Related Issues

Daily official-theme run (Matcha, Phosphor, Fog).

## Checklist

- [x] I've read [CONTRIBUTING.md](../CONTRIBUTING.md)
- [x] Tests pass locally (`pnpm --filter @dripnex/desktop test` 303,
`pnpm --filter @dripnex/plugin-api test` 255). Full `pnpm test` failed
only on unrelated `@dripnex/mcp-server` FTS5 (`no such module: fts5` in
this environment)
- [x] Desktop + plugin-api typecheck succeed
- [x] PR targets `develop` branch (not `main`)

## Screenshots

N/A — token palettes only. Preview in Settings → Themes after this
lands.

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-4779a3f7-42ad-426d-ab28-cde61d50e8d0?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-4779a3f7-42ad-426d-ab28-cde61d50e8d0&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
  - Added three official themes: Matcha, Phosphor, and Fog.
- Each theme includes tailored interface and status colors for light or
dark mode.

- **Documentation**
- Added an official theme log documenting the new palettes and their
color schemes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@github-actions github-actions Bot added size/L and removed size/M labels Aug 22, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
First visual polish pass after v0.16.0. AuthGate stays (ADR 002 / #545).
No new product surfaces.

**Visual source of truth is in-repo only:** `tokens.css`, existing
desktop CSS/primitives, and `docs/VISUAL_LANGUAGE.md`. Confirmed 22 Aug
2026: `dripnex.app` is a GoDaddy parking page (black CTA, pale-blue
tiles, Helvetica). `dripnex.github.io/readide` is 404. This PR does
**not** match either. No new palette.

## Visual changes

### AuthGate (worst first-run screen)
- **Before:** Hardcoded zinc `#09090b` screen, blue-tinted mesh
(`#152036`), 16px card radius, inverted ink/black submit
(`--text-primary` on `--bg-base`), weak input focus, always-dark.
- **After:** Tokens throughout (`--bg-base`, `--glass-bg`,
`--radius-xl`, `--font-sans`). Mesh colors come from surface tokens and
follow `data-color-scheme`. Submit matches in-app `Button` primary
(`--accent` / `--accent-hover`), not a parking-page black CTA. Input
focus matches the Input primitive. Signup copy drops “Free to start.”
- **Why:** First-run was a one-off dark lander. Black CTA would copy
GoDaddy, not the app. Flow is unchanged.

### Empty states
- **Before:** 200px stub, Sparkles icon, “No matches found,” pinned
filter reused “No notes yet,” Mac-only ⌘N, 48px editor icon.
- **After:** Centered 28px faint icon + 14/500 title + 12px hint.
`modAccel()` prints ⌘ or Ctrl. Pinned empty is “Nothing pinned.” Search
is “No matches.”
- **Why:** Sparkles and “found” are energetic.
Pinned-with-notes-but-none-pinned was a copy bug.

### Note list chrome
- **Before:** Header 16/600, row pad 12×16, accent timestamps, no search
focus ring, stagger ignored reduced motion.
- **After:** `--text-lg` / 500 titles, 10×14 rows, muted timestamps,
Input-style search ring, `prefers-reduced-motion`.
- **Why:** Accent on every timestamp steers the eye. Header was shoutier
than the notes.

### Editor chrome
- **Before:** Title turned accent on focus (16/600). Toolbar 32px
hovering `--bg-elevated` on an elevated pane.
- **After:** Title stays `--text-primary` at 14/500. Toolbar 28×28,
hover `--bg-hover`.
- **Why:** Title-as-accent is color directing. Toolbar was a size off
the 28px chrome controls.

## Docs
`docs/VISUAL_LANGUAGE.md` now records:
- Source order: tokens → this file → primitives → BRAND.md voice
- Explicit **not** sources: parking page, GitHub Pages 404, archived
violet/Raycast draft
- Type, spacing, color, empty-state voice, AuthGate / list / editor
rules
- `tokens.css` points at that file

## Remaining issues filed
- #557 Nested list marks use blue and violet
- #558 Settings MagicLinkFlow does not match AuthGate chrome
- #559 Welcome wordmark and frosted accent wash need a brand call

## Tomás — brand calls, not guessed
- **Accent hue:** shipped teal (`#5eead4` / `#0d8a80`) vs `BRAND.md`
muted gray. Left teal alone.
- **AuthGate mesh:** quieted and tokenized; brand says no gradients.
Keep moving wash or a still surface?
- **Font stack:** `--font-sans` still includes Helvetica Neue as a
fallback. Did not promote Helvetica to first family.
- **Logo / wordmark:** AuthGate still uses `logo.png` at 40px. Welcome
still uses the accent wordmark (#559).
- **Site:** parking page + 404. Real site is #546. Not this PR.

## Screenshots
Could not capture the Electron renderer here. Exact surfaces: AuthGate
sign-in / sign-up / hydrating / sent; list empty / pinned / trash /
search; editor empty, title focus, toolbar hover.

## Checklist
- [x] AuthGate kept
- [x] Parking page / 404 not used as visual reference
- [x] No new product features or palette
- [x] No CI / release / version / iOS / Psynth / Linear
- [x] No new tests
- [x] Targets `develop`
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-e1f2eaa1-e66c-4829-babc-c5e71deb73a6?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-e1f2eaa1-e66c-4829-babc-c5e71deb73a6&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@github-actions github-actions Bot added size/XL and removed size/L labels Aug 22, 2026
tomymaritano and others added 4 commits August 22, 2026 21:00
## Summary

Documents the MCP / other-AI plan in a new folder `docs/ai/` (plan only,
no product code).

- `docs/ai/README.md` index
- `docs/ai/mcp-plan.md` Inkdrop shape: local HTTP after AuthGate + one
MCP; Grok Bot / Claude / Cursor share it
- `docs/ai/updates.md` 22 Aug decisions (AuthGate stays, #541/#552,
Project 3, no CouchDB, mobile Later)

Does not implement MCP HTTP. Existing `packages/mcp-server` stays as the
current stdio path.

## Test plan

- [ ] Read the three files on the PR
- [ ] Confirm no app/CI files changed
…rap (#575)

## Problem

Creating a sync passphrase fails with `Error: Unknown cipher`.

`encryptionService.wrapKey()` called `createCipheriv('aes-256-wrap',
...)`. That cipher exists in **OpenSSL** but **not in BoringSSL**, which
is what Electron ships. The call therefore always threw in the main
process and the CEK could never be wrapped.

Reproduced directly:

| Runtime | `aes-256-wrap` |
| --- | --- |
| Node 20 (OpenSSL 3.0.17) | available |
| Electron 41.7.1 (BoringSSL, `process.versions.openssl === '0.0.0'`) |
**`Unknown cipher`** |

Vitest never caught this because it runs on Node/OpenSSL, where the
cipher exists.

## Fix

New `aesKeyWrap.ts` implements RFC 3394 over the raw AES block primitive
BoringSSL *does* expose. `encryptionService` delegates to it.

Output is **byte-identical** to `aes-256-wrap`, so keys wrapped by
earlier builds still unwrap — pinned by the RFC 3394 §4.6 known-answer
vector (which I generated from OpenSSL's own `aes-256-wrap` before
writing the implementation).

`aes-256-ecb` here is the single-block AES call the spec is defined on,
with padding disabled — not a mode for bulk data. The chaining, counter
and integrity check are RFC 3394's. This is documented at the top of the
module.

Neither `webcrypto.subtle` AES-KW (`Unrecognized algorithm name`) nor
any other built-in wrap is available in Electron, so reimplementing was
the only option that preserves the on-disk format.

## Verification

- 6 new unit tests: KAT wrap, KAT unwrap, overhead size, tampered
ciphertext rejected, wrong KEK rejected, cipher-name guard against a
revert to `aes-256-wrap`.
- Executed under the **Electron 41.7.1 binary**
(`ELECTRON_RUN_AS_NODE=1`): wrap matches the vector, round-trip
succeeds, wrong key rejected.
- `pnpm test` — 19/19 tasks green.
- `pnpm --filter @dripnex/desktop typecheck` — clean.
- `eslint` — clean.

## Note for a separate PR

`.nvmrc` pins Node 20, but `packages/mcp-server` needs >= 22.5
(`node:sqlite`) and `packages/api` needs >= 22 (wrangler). On Node 20
`pnpm test` and `pnpm dev` both fail for reasons unrelated to this
change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Improved encrypted key wrapping and unwrapping compatibility.
* Corrected passphrase handling so invalid or corrupted encrypted data
is reliably rejected.
  * Added validation for malformed keys and tampered encrypted content.

* **Tests**
* Added coverage for standard AES key-wrapping compatibility, successful
recovery, wrong passphrases, and modified data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Wire `@dripnex/mcp-server` to Dripnex Local HTTP so Cursor / Grok /
Claude talk Bearer loopback instead of opening SQLite when the HTTP env
is set. Settings → Integrations → MCP now copies that same HTTP env.

- Prefer `DRIPNEX_LOCAL_SERVER_URL` + `DRIPNEX_LOCAL_TOKEN` (loopback
only). SQLite (`DRIPNEX_DB_PATH`) remains the fallback when those are
unset.
- Map existing tools onto the real routes in `localServer.ts` (notes,
search, books=notebooks, tags).
- Add the missing HTTP surfaces: `dripnex_status`, notebook CRUD, tag
create/update, `dripnex_get_changes`.
- Soft-delete vs hard-delete follows the desktop handler: `DELETE
/api/notes/:id` is trash; `?permanent=1` is hard delete
(`dripnex_trash_note` `permanent`).
- Writes stay gated by Settings → Integrations → Allow writes
(`mcp.json`) / `DRIPNEX_MCP_WRITES`. Enabling Local HTTP is **not** a
write grant (verified: `httpApiEnabled` vs `mcpWrites` are separate).
Flipping Allow writes does not change the copied snippet.
- **Settings snippets:** Claude Code / Codex copy
`DRIPNEX_LOCAL_SERVER_URL` + `DRIPNEX_LOCAL_TOKEN` (the URL and token
already shown in the panel) when Local HTTP or MCP is on.
`DRIPNEX_DB_PATH` is only used when HTTP is off. The launch path stays
the real `mcp-server` entry when the app knows it; otherwise
`/ABS/PATH/...` remains a placeholder — HTTP env is still preferred.
- HTTP client unit tests use mocked `fetch`. HTTP store mapping is
covered with a stub client. Snippet tests use `placeholder-token` and
`/tmp` paths only.

`docs/ai/` is not on `develop` — it lives on `docs/ai-mcp-plan`. Env and
Cursor/Grok config are in `packages/mcp-server/README.md` (placeholders
only; no tokens).

## Type of Change

- [x] New feature
- [x] Documentation update

## Checklist

- [x] PR targets `develop` branch (not `main`)
- [x] MCP tests: 35 passed (http client/config/store, writes, notes,
sqliteRepo)
- [x] Snippet tests: 7 passed
- [x] Monorepo `typecheck` passed on push
- [ ] `fts5-triggers` fails in this cloud Node build (`no such module:
fts5`) — pre-existing, file not changed

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-487fd9fa-1c5e-4511-9bda-c020a67bd6ed?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-487fd9fa-1c5e-4511-9bda-c020a67bd6ed&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

Non-draft PRs into `develop` now get `gh pr merge --auto --squash`.
`--auto` waits for required status checks, so red CI does not merge.
Drafts stay out. PRs targeting a feature branch stay out (no stacked-PR
instant merge). Dependabot uses the same rule. No `automerge` label.

## Type of Change

- [x] Documentation update
- [x] CI / repo rule (not a product feature)

## What changed

- `.github/workflows/automerge.yml` — one rule for every non-draft PR
into `develop`; disable auto-merge if the PR is drafted or retargeted
off `develop`.
- `docs/ci.md` + `docs/adr/006-ci-green-automerge.md` — CI green on
develop = squash auto-merge.
- `docs/RELEASE.md` — one-line pointer to that rule.

## Repo settings

- `allow_auto_merge` is **on**.
- Develop ruleset already requires `lint`, `test`, `typecheck`,
`security`, `commitlint`. Auto-merge cannot fire without those green.
- `--auto` is enabled on this PR and on 561, 560, 554, 553 (Dependabot
PRs already had it). They still only merge when required checks are
green.

This token cannot PATCH rulesets (403). CodeRabbit is still a required
check, and `e2e` is not yet required. Drop CodeRabbit, add `e2e`, and
turn off review-thread resolution (no reviews / no CodeRabbit gate) on
ruleset `13807741`:

```bash
gh api --method PUT repos/dripnex/app/rulesets/13807741 --input - <<'EOF'
{
  "name": "develop",
  "target": "branch",
  "enforcement": "active",
  "conditions": { "ref_name": { "exclude": [], "include": ["refs/heads/develop"] } },
  "rules": [
    { "type": "deletion" },
    { "type": "non_fast_forward" },
    {
      "type": "pull_request",
      "parameters": {
        "required_approving_review_count": 0,
        "dismiss_stale_reviews_on_push": true,
        "require_code_owner_review": false,
        "require_last_push_approval": false,
        "required_review_thread_resolution": false,
        "require_extra_approval_for_unattributed_changes": false,
        "allowed_merge_methods": ["squash", "merge", "rebase"]
      }
    },
    {
      "type": "required_status_checks",
      "parameters": {
        "strict_required_status_checks_policy": false,
        "do_not_enforce_on_create": false,
        "required_status_checks": [
          { "context": "lint" },
          { "context": "test" },
          { "context": "typecheck" },
          { "context": "e2e" },
          { "context": "security" },
          { "context": "commitlint" }
        ]
      }
    }
  ]
}
EOF
```

Not deploy. Do not require reviews or CodeRabbit.

## Related Issues

Tomás: auto-merge Dripnex PRs automatically ONLY if CI passes. No review
required. No automerge label.

## Checklist

- [x] PR targets `develop` branch (not `main`)
- [x] No marketplace/product changes

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-4c013d92-1b7f-4f10-ad90-a79e531700cf?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-4c013d92-1b7f-4f10-ad90-a79e531700cf&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Automation**
* Non-draft pull requests targeting `develop` are now automatically
squash-merged after required CI checks pass.
* Draft pull requests and pull requests retargeted away from `develop`
are excluded from auto-merge.

* **Documentation**
* Updated release and CI documentation to clarify the auto-merge policy,
required checks, and exclusions.
  * Added an architectural decision record documenting the policy.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tomymaritano tomymaritano changed the title ci: copy #570 stamp/re-draft onto main chore(release): promote 0.17.0 Aug 22, 2026
Back-merge so the promotion PR #571 can satisfy `main`'s up-to-date
requirement.

`main` carries release-tooling commits `develop` never received —
notably #540 (tag without pushing a bump commit) and #537 (point
semantic-release at `dripnex/app`). `main` is authoritative for release
tooling, so those come back here.

**Why a branch and not `gh pr update-branch`:** GitHub refuses to update
a PR whose head is a protected branch. And a direct `develop ← main` PR
(#577) deadlocks — `develop` requires the head to be up to date, but
`main` is inherently behind `develop`. A branch descended from `develop`
with `main` merged in satisfies both.

No product changes: release config, workflow, and doc URLs only.
`package.json` stays at 0.15.2, which is expected under the post-#540
flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Establishes `main` as an ancestor of `develop` so the promotion PR #571
can satisfy `main`'s up-to-date requirement.

**This must be merged with a merge commit, not squashed.** #578 squashed
the same content and that is exactly why it did not work: a squash
replays the changes as a new commit, so `main`'s commits never become
ancestors of `develop` and #571 stayed BEHIND. `git merge-base
--is-ancestor origin/main origin/develop` still failed after it.

`git diff origin/develop` on this branch is **empty** — #578 already
brought the content across. This PR carries ancestry only.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
tomymaritano and others added 2 commits August 22, 2026 22:10
## Problem

#566 made every non-draft PR into `develop` squash auto-merge. That
breaks the release flow, and I found it by hitting it twice.

A back-merge exists for its **topology**, not its content. Squashing one
replays `main`'s changes as a fresh commit, so `main` never becomes an
ancestor of `develop`. `main`'s ruleset has
`strict_required_status_checks_policy: true`, so the promotion PR #571
stays `BEHIND` forever.

Both #578 and #579 hit it. On #579 I explicitly ran `gh pr merge --auto
--merge`, and the workflow re-armed auto-merge with `--squash` and
overrode it. The resulting commit `00ac6d5` has a single parent, and
`git merge-base --is-ancestor origin/main origin/develop` still fails.

## Fix

Skip PRs whose head branch starts with `chore/backmerge-`. Those get
merged by hand with a merge commit.

Also documents the back-merge recipe in `docs/RELEASE.md` next to the
promotion steps, since the failure mode is silent — the PR just sits at
BEHIND with every check green and no explanation.

## Not changed

The auto-merge policy itself, permissions, and the
concurrency/live-recheck hardening from earlier in #566 all stay as they
are. This is one `if:` clause plus docs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): merge main into develop
@tomymaritano
tomymaritano merged commit d13acfc into main Aug 22, 2026
24 of 25 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.17.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

github-actions Bot pushed a commit that referenced this pull request Aug 23, 2026
## What removing `|| true` revealed

The `disable-automerge` job fails with:

```
FORBIDDEN — Resource not accessible by integration
```

It had been failing silently behind `|| true` (see run
[32602609796](https://github.com/dripnex/app/actions/runs/32602609796)
on #571). The safety net that is supposed to stop a queued auto-merge
from landing on an unprotected base **has never actually worked** — it
just reported green.

## Honest note on cause

I cannot prove this was pre-existing. The job lost `contents: write`
earlier in #566 when I scoped workflow-level permissions per job on a
least-privilege review, and `|| true` meant no run before or after ever
surfaced a failure. Two live hypotheses:

1. The mutation needs `contents: write` and the least-privilege
narrowing broke it.
2. It was already FORBIDDEN and the suppression hid it.

This PR tests hypothesis 1 by granting the scope back. If FORBIDDEN
returns with both scopes present, the cause is a repository or org
restriction on `GITHUB_TOKEN` and needs a settings change or a PAT — the
comment in the file says so, so the next person does not have to
rediscover it.

## Scope narrowing

The job fired on every PR whose base was not `develop`, which includes
release promotions into `main`. `main` is protected; there is no unsafe
queued merge to undo there. The risk this job exists for is landing on
an **unprotected** base, so it now fires only on drafts, or on a base
that is neither `develop` nor `main`.

## Not changed

The loud failure stays. Going back to `|| true` would restore exactly
the false confidence that hid this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated automated merge handling for draft pull requests.
* Protected pull requests targeting `main` and `develop` from automatic
processing.
* Enabled the required repository content permissions for the automation
workflow.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant