Skip to content

refactor(main): extract FileLicenseStorage and window state to services (PR-G)#278

Merged
github-actions[bot] merged 1 commit into
feat/playwright-electron-e2efrom
refactor/extract-from-main-index
Jun 8, 2026
Merged

refactor(main): extract FileLicenseStorage and window state to services (PR-G)#278
github-actions[bot] merged 1 commit into
feat/playwright-electron-e2efrom
refactor/extract-from-main-index

Conversation

@tomymaritano

Copy link
Copy Markdown
Collaborator

Summary

First scoped slice of the PR-G "split god files" effort.

`apps/desktop/src/main/index.ts` goes from 1065 → 950 lines (-11%). No behavior change. All extractions are self-contained and surface-area preserving — no API changes, no runtime touch.

What moved

New file Was at Notes
`services/fileLicenseStorage.ts` `index.ts:146–216` Three `readJsonOrNull` helpers fold the repeated try/catch/return-null pattern into one private function. Header comment points at PR-F3 so the next step (move to signed envelopes) is unambiguous.
`services/windowState.ts` `index.ts:218–254` Header comment now documents why the file I/O is synchronous (called during `BrowserWindow` construction before the renderer mounts, and during close where handlers don't await).

What this PR DELIBERATELY does NOT touch

Per your earlier guidance about merge-conflict risk:

File Why deferred
`packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts` (1121 L) Splitting into `NoteCrudRepository` + `NoteTagRepository` + `NoteArchiveRepository` + `NoteSyncRepository` needs runtime verification against a real DB. Risk of merge conflicts with every other in-flight PR. Separate effort.
`apps/desktop/src/renderer/components/MarkdownEditor.tsx` (724 L) The CodeMirror surface is too entangled to split without an E2E suite to catch regressions. Defer until PR-E (#277) is stabilized; then refactor under test coverage.

The principle is established here: surgical extractions of self-contained pieces, each verifying typecheck+tests, no behavior change. Future PRs can keep slicing under the same discipline.

Test plan

  • `pnpm -r typecheck` — green
  • `pnpm test` — 17/17 packages
  • `pnpm --filter @readied/desktop typecheck` — green (includes the e2e tsconfig from feat(e2e): scaffold Playwright Electron suite + CI job (PR-E) #277)
  • Manual smoke: launch the app, confirm window remembers its position after close+reopen, confirm license/trial files still read/write correctly

Stack context

PR-G in the audit stack. Stacked on top of #277 (PR-E) → #276 (PR-F3) → #275 (PR-F2) → #274 (PR-F5) → #273 (PR-F4) → #272 (PR-F1) → #271 (PR-I) → #270 (PR-J) → #269 (PR-D) → #268 (PR-H) → #267 (PR-C) → #266 (PR-A) → #265 (PR-B). 14 PRs deep.

🤖 Generated with Claude Code

First scoped slice of the PR-G "split god files" effort.

apps/desktop/src/main/index.ts goes from 1065 -> 950 lines (-11%).
No behavior change. All extractions are self-contained and surface-area
preserving:

- services/fileLicenseStorage.ts — was embedded in index.ts at lines
  146-216. The three readJsonOrNull helpers fold the repeated
  try/catch/return-null pattern into one private function. The TODO
  about signed subscription envelopes points back at PR-F3 so the
  next step is unambiguous.

- services/windowState.ts — was at lines 218-254 of index.ts.
  Comment now explicitly documents why the file I/O is sync (called
  during BrowserWindow construction before the renderer mounts and
  during close where handlers don't await).

What this PR DELIBERATELY does NOT touch:

- packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts
  (1121 lines). Splitting this into NoteCrudRepository +
  NoteTagRepository + NoteArchiveRepository + NoteSyncRepository
  needs runtime verification against a real DB and risks merge
  conflicts with every other PR in flight. Separate effort.
- apps/desktop/src/renderer/components/MarkdownEditor.tsx (724
  lines). The CodeMirror surface area is too entangled to split
  without an E2E suite to catch regressions. Defer until PR-E
  (#277) is stabilized and we can refactor under test coverage.

Validates:
- pnpm -r typecheck — green
- pnpm test — 17/17 packages
- pnpm --filter @readied/desktop typecheck — green (includes e2e
  tsconfig)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
readide Building Building Preview, Comment Jun 8, 2026 2:02pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • develop

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aad839a1-a048-45bc-965d-b38f8154c55a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/extract-from-main-index

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 and usage tips.

@github-actions github-actions Bot merged commit 7b21ff5 into feat/playwright-electron-e2e Jun 8, 2026
5 of 6 checks passed
@github-actions github-actions Bot added the size/L label Jun 8, 2026
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

First slice of the knip cleanup. Knip surfaced 28 unused files in #267.
After per-file verification (greps, import tracing, barrel resolution),
**12 were genuinely orphaned** — those are deleted here. The rest stay
for reasons documented below.

## Deleted (12 files, 659 lines)

### \`apps/desktop\`

| File | Why safe |
|---|---|
| \`src/renderer/analytics.ts\` | No imports anywhere |
| \`src/renderer/hooks/useTheme.ts\` | No imports anywhere |
| \`src/renderer/settings.tsx\` | No imports anywhere (the real settings
entry is \`pages/settings/SettingsApp.tsx\`, loaded via dynamic import
in \`main.tsx\`) |
| \`src/renderer/ui/patterns/Modal.tsx\` + \`index.ts\` +
\`Modal.module.css\` + \`.gitkeep\` | Only the barrel imported
\`Modal\`, and the barrel itself was unused — both go together |

### \`packages\`

| File | Why safe |
|---|---|
| \`plugin-api/src/editor/types.ts\` | Not re-exported by
\`plugin-api/src/index.ts\` |
|
\`storage-core/src/{interfaces,migrations,repositories,types}/index.ts\`
| Main package index imports directly from concrete files, not these
barrels |
| \`storage-sqlite/src/repositories/index.ts\` | Same — concrete
imports, no barrel use |

### \`scripts\`

| File | Why safe |
|---|---|
| \`scripts/bump-version.js\` | Not referenced by any \`package.json\`
script or CI workflow |

## Kept (Knip false positives)

### Auto-discovered files Knip can't see

| File | Why kept |
|---|---|
| \`apps/desktop/src/renderer/vite-env.d.ts\` | \`/// <reference
types="vite/client" />\` — required by Vite |
| \`apps/desktop/src/renderer/css-modules.d.ts\` | TypeScript module
shim for CSS module imports |
| \`apps/desktop/src/renderer/turndown-plugin-gfm.d.ts\` | TypeScript
module shim for an untyped npm dep |
| \`apps/web/mdx-components.tsx\` | Next.js convention —
auto-discovered, never imported |

### Knip wrong about being unused

| File | Reality |
|---|---|
|
\`renderer/pages/settings/components/controls/{NumberInput,Select,TextInput,Toggle}.tsx\`
| Imported by EditorSection / AiSection / UpdatesSection **through** the
\`controls/index.ts\` barrel. Knip flagged both the components and the
barrel as unused because it doesn't follow the chain. |

## Skipped (out of audit scope)

apps/web cleanup: \`magicui/*\`, \`NavDropdown\`, \`ui/separator\`. The
audit excluded apps/web; leaving them for a separate marketing-site
pass.

## Test plan

- [x] \`pnpm -r typecheck\` — green
- [x] \`pnpm test\` — 17/17 packages
- [ ] Manual smoke after merge

## What's next in this cleanup track

- **PR-Knip-2**: remove unused production + dev dependencies (the
\`Unused dependencies (9)\` block from knip)
- **PR-Knip-3**: remove unused exports (the \`Unused exports (~100)\`
block) — much more careful, since some exports may be public API
contracts for plugins or external consumers

## Stack context

Stacked on top of #278 (PR-G) → #277 (PR-E) → #276 (PR-F3) → #275
(PR-F2) → #274 (PR-F5) → #273 (PR-F4) → #272 (PR-F1) → #271 (PR-I) →
#270 (PR-J) → #269 (PR-D) → #268 (PR-H) → #267 (PR-C) → #266 (PR-A) →
#265 (PR-B). **15 PRs deep.**

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

Phase 2 of knip cleanup. Knip flagged 11 unused dependencies in #267;
per-dep verification confirmed **6 are genuinely unused** in our scope.
Removing them shrinks the lockfile by ~120 lines.

## Removed

### \`apps/desktop\` production

| Dep | Why safe |
|---|---|
| \`highlight.js\` | We use \`rehype-highlight\`, which depends on
\`lowlight\` (NOT \`highlight.js\` directly). \`lowlight\` ships its own
grammars. |
| \`pino-roll\` | \`logger.ts\` writes directly via
\`createWriteStream\` — no pino transport / no rolling pipeline |
| \`react-resizable-panels\` | No imports anywhere |
| \`unist-util-visit\` | No imports anywhere (the only consumer is
\`@readied/wikilinks\`, which declares it itself) |

### \`apps/desktop\` dev

| Dep | Why safe |
|---|---|
| \`@types/mdast\` | No imports anywhere |
| \`pino-pretty\` | \`logger.ts\` doesn't pipe through a transport; dev
output is raw JSON via the synchronous pino factory |

### \`packages\`

| Package | Dep removed | Why |
|---|---|---|
| \`@readied/sync-core\` | \`@readied/core\` | No imports of
\`@readied/core\` in \`src/*\` — workspace link was stale |
| \`@readied/wikilinks\` | \`unified\` | \`src/*\` uses
\`unist-util-visit\` directly (kept) but never \`unified\` |

## Skipped (out of audit scope)

\`apps/web\`: \`@radix-ui/react-separator\`, \`next-themes\`,
\`tailwindcss-animate\` — marketing site, separate pass.

## Test plan

- [x] \`pnpm install --ignore-scripts\` — succeeds. Pre-existing peer
warnings about electron-builder / electron-vite are unrelated.
- [x] \`pnpm -r typecheck\` — green.
- [x] \`pnpm test\` — 17/17 packages.
- [ ] Manual smoke on the desktop app after merge — verify nothing
transitively depended on highlight.js / pino-roll / etc. that grep
missed.

## Stack context

Stacked on **PR-Knip-1** (#279) → PR-G (#278) → PR-E (#277) → ... down
to PR-B (#265). 16 PRs deep.

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
…ge layer (PR-F3-Wiring) (#281)

## Summary

Wires the verification primitive from #276 into the desktop's
\`FileLicenseStorage\`. The path from server → disk → in-memory now has
a real Ed25519 verification step at read time, with **lenient
fallthrough** during the migration window.

## Changes

### \`packages/licensing/src/types.ts\`

\`StoredSubscriptionData\` now allows an optional \`signedEnvelope\`:

\`\`\`ts
interface StoredSubscriptionData {
  readonly subscription: SubscriptionInfo;
  readonly lastVerified: string;
  readonly cacheExpiresAt: string;
  readonly signedEnvelope?: SignedSubscriptionEnvelope; // NEW
}
\`\`\`

Both can coexist during migration. Long-term, the unsigned
\`subscription\` field becomes a derived view of the envelope payload.

### \`apps/desktop/src/main/services/fileLicenseStorage.ts\`

\`readSubscriptionData\` now branches on envelope presence:

| State on disk | Behavior |
|---|---|
| **Envelope present + signature valid** | Return cache as-is |
| **Envelope present + signature invalid** | Log error, **refuse the
cache** (return null). Next caller fetches fresh from the API |
| **Envelope absent** | Log warning, accept the cache (lenient migration
mode) |

Plus a \`SUBSCRIPTION_PUBLIC_KEY\` constant at the top of the file with
a **REPLACE BEFORE SHIPPING** note. The all-zeros placeholder means any
real envelope will fail verification — that's the *correct* failure mode
while the placeholder is in place. The lenient "no envelope" branch is
what runs today.

## What's NOT in this PR (still pending the server team)

- \`ApiClient.getSubscriptionStatus\` does not yet return an envelope.
When it does, \`mapApiToSubscriptionData\` in \`licenseHandlers.ts\`
grows one more field (\`signedEnvelope\`) and \`writeSubscriptionData\`
persists it. That change is one-liner plumbing, blocked only on the API
contract being updated.
- The placeholder public key gets swapped for the real server key in the
release that first ships signed envelopes.

## Behavior matrix

| Disk state | Today (placeholder key) | After server rollout (real key)
|
|---|---|---|
| Empty | null returned, no log | null returned, no log |
| Old shape (no envelope) | Warning logged, accepted | Warning logged,
accepted (until strict mode) |
| Real envelope, valid sig | Cannot occur (server isn't signing yet) |
Silently accepted |
| Real envelope, invalid sig | Will fail (placeholder key) → refused →
refetch | Refused → refetch |
| Tampered envelope | Refused → refetch | Refused → refetch |

## Test plan

- [x] \`pnpm -r typecheck\` — green
- [x] \`pnpm test\` — 17/17 packages (the 18 signature tests from #276
cover the verify path; this PR just calls into them)
- [ ] Manual: edit \`subscription.json\` to flip a single byte in the
cached \`subscription.subscriptionId\` field. Reopen the app. Once
server signs envelopes: expect error log + refetch. Today: warning log +
cache accepted (no envelope to verify).

## Stack context

Stacked on **PR-Knip-2** (#280) → PR-Knip-1 (#279) → PR-G (#278) → PR-E
(#277) → ... down to PR-B (#265). **17 PRs deep.**

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
…Repository (#282)

## Summary

**Phase 1 of the SQLiteNoteRepository split.** Pure helpers come out to
their own file so future sub-repositories (sync, tag, archive) can reuse
them without inheriting from the 1000-line class.

| Metric | Before | After |
|---|---|---|
| \`SQLiteNoteRepository.ts\` | 1121 lines | **1038 lines** (-7%) |
| \`noteMapping.ts\` | — | 133 lines (new) |

## Extracted

| Symbol | Kind | Notes |
|---|---|---|
| \`NoteRow\`, \`TagRow\`, \`TagWithColorRow\`, \`BacklinkInfo\` | Row
types | Re-exported by SQLiteNoteRepository so external imports keep
working |
| \`rowToNote(row, tags) -> Note\` | Pure mapper | Reconstructs a domain
Note from a SQLite row + its tags |
| \`prepareFtsQuery(input) -> string\` | Pure helper | FTS5 query
escaper + tokenizer |
| \`archivedConditionSql(filter, alias) -> string\` | Pure helper | SQL
fragment for archived filtering |

Call sites swapped from \`this.<helper>()\` to plain function imports.
**The public class signature is unchanged** — \`BacklinkInfo\` is
re-exported so external consumers (e.g.
\`apps/desktop/src/main/handlers/types.ts\`) keep working without edits.

## What this PR DELIBERATELY does NOT do

- **Extract sync methods** (\`getPendingChanges\` through
\`getSyncHistory\` ~430 lines) into a \`SQLiteNoteSyncRepository\`.
Those share state — tag queries, transactions, FTS sync triggers — with
the main class in ways that need real-DB integration coverage to
refactor safely. The helpers extracted here are the foundation: a
follow-up PR can build the sync sub-repo on top of them without touching
the helpers again.
- **Extract tag methods** (\`setManualTags\`, \`renameTag\`,
\`getAllTagsWithColors\`, etc.) for the same reason.

The audit aspired to a 4-way split (NoteCrudRepository +
NoteTagRepository + NoteArchiveRepository + NoteSyncRepository). That
remains the destination. This PR ships the **foundation** that makes
those splits low-risk; each can ride its own PR with focused review.

## Test plan

- [x] \`pnpm -r typecheck\` — green
- [x] \`pnpm test\` — 17/17 packages
- [ ] Manual: launch the desktop, exercise notes CRUD + search + tag
operations. Behavior should be identical (no observable change).

## Stack context

Stacked on **PR-F3 wiring** (#281) → PR-Knip-2 (#280) → PR-Knip-1 (#279)
→ PR-G (#278) → PR-E (#277) → ... down to PR-B (#265). **18 PRs deep.**

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

**Phase 1 of the MarkdownEditor split.** Pure theme + markdown
HighlightStyle move to their own file so further extractions (extensions
array, keymap bindings) can ride on top without merging against a
churn-prone component shell.

| Metric | Before | After |
|---|---|---|
| \`MarkdownEditor.tsx\` | 737 lines | **612 lines** (-17%) |
| \`editorTheme.ts\` | — | 139 lines (new) |

## Extracted

| Symbol | Kind |
|---|---|
| \`SCROLL_PAST_END_PADDING\` | constant |
| \`createEditorTheme(fontSize, fontFamily, lineHeight)\` | factory
returning \`EditorView.theme({...})\` |
| \`markdownHighlighting\` | \`HighlightStyle.define([...])\` with all
tag styles for markdown |

## Imports cleaned up

- Drop \`HighlightStyle\` from \`@codemirror/language\` import (no
longer referenced in this file)
- Drop \`tags\` from \`@lezer/highlight\` (moved into editorTheme.ts)

## What this PR DELIBERATELY does NOT do

- **Extract the extensions array** (~96 lines of \`createExtensions\`).
It closes over user settings (\`lineNumbersCompartment\` etc.) and mixes
context-coupled values like \`wikilinkAutocomplete\` from a hook. Safely
pulling that out requires either passing the closure context via a
builder, or moving the whole \`useMemo\` into its own hook. **Better
done under Playwright coverage (PR-E #277) so renderer regressions
surface.**
- **Extract the keymap.** Same reason — bindings reference
view-imperatives + the command-registry which are constructed inside the
React tree.

## Test plan

- [x] \`pnpm -r typecheck\` — green (renderer + e2e tsconfigs)
- [x] \`pnpm test\` — 17/17 packages
- [ ] Manual: open the editor, type in a markdown note with headings,
emphasis, lists, code blocks. The look must be identical (theme is
byte-for-byte the same; just moved).

## Stack context

Stacked on **note repo split** (#282) → PR-F3 wiring (#281) → PR-Knip-2
(#280) → PR-Knip-1 (#279) → PR-G (#278) → PR-E (#277) → ... down to PR-B
(#265). **19 PRs deep.**

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

Promotes `develop` to `main` for **v0.15.0**. Originally opened
2026-04-24; now refreshed with the 19-PR tech-debt audit shipped via
#285 plus the accumulated dependabot bumps reconciled.

`semantic-release` will pick the version bump. Expected: **minor
(v0.14.x → v0.15.0)** because of multiple \`feat:\` commits.

## What ships (audit highlights, from #285)

### Runtime fixes (user-facing)
- **Editor no longer crashes on table-containing notes** (#266) —
\`Decoration.replace\` over multi-line ranges moved from a ViewPlugin to
a StateField, plus an EditorView.exceptionSink so any future plugin
error no longer tears down the EditorView.
- **AI keys survive sleep/wake** (#275) — \`aiKeyStorage\` stopped
silently deleting the encrypted store when the keychain was temporarily
locked after macOS sleep.
- **Backup restore is now safe** (#271) — restored DBs go through
\`PRAGMA integrity_check\` before being swapped in; corrupt backups roll
back to the safety copy.
- **MCP server runs without electron-builder rebuilds** (#264#270) —
migrated from native \`better-sqlite3\` to built-in \`node:sqlite\`
(Node 22.5+), updated to the new \`registerTool\` MCP SDK API.

### Security
- **Typed IPC boundary** (#272 + #273 + #274) — 130+ IPC channels now
validated with Zod tuples at the main↔renderer boundary. Garbage in
fails fast with \`IpcValidationError\` instead of corrupting downstream
code.
- **Ed25519 license verification scaffolding** (#276 + #281 + #284) —
\`signSubscriptionPayload\` / \`verifySubscriptionSignature\` helpers
ship in \`@readied/licensing\`, wired into
\`FileLicenseStorage.readSubscriptionData\` with lenient fallthrough
during migration. Real public key embedded (\`d04901…\`). Server-side
\`LICENSE_SIGNING_PRIVATE_KEY\` already set in Cloudflare staging +
production.

### Developer experience
- **Husky → Lefthook** (#267) plus lint-staged that now runs ESLint, not
just Prettier.
- **\`knip\` added** (#267) + 12 unused files deleted (#279) + 6 unused
deps dropped (#280).
- **Playwright Electron E2E scaffold** (#277) with smoke + notes-IPC
specs and a Linux+xvfb CI job (\`continue-on-error: true\` while it
stabilises).
- **Vitest coverage baseline** (#269) — 12 packages share a coverage
config; smoke tests added for \`@readied/commands\`.

### Refactor (no behavior change)
- **Zustand selectors migration** (#268) — 3 components stopped
destructuring entire stores.
- **God-file extractions**:
- \`main/index.ts\` 1065 → 950 lines (#278) — \`FileLicenseStorage\`,
\`windowState\` extracted to services
- \`SQLiteNoteRepository.ts\` 1121 → 1038 lines (#282) — pure helpers
extracted to \`noteMapping.ts\`
- \`MarkdownEditor.tsx\` 737 → 612 lines (#283) — theme +
markdownHighlighting extracted to \`editorTheme.ts\`

## Deploys triggered

| Workflow | Trigger | What happens |
|---|---|---|
| \`deploy-api.yml\` | Auto on \`push\` to main affecting
\`packages/api/**\` | Tests + deploys \`@readied/api\` to Cloudflare
Workers (\`readied-api-production\`). This stack only touched
\`wrangler.toml\` + \`.dev.vars\` docs, no production code change. |
| \`release.yml\` | Manual \`workflow_dispatch\` post-merge |
\`semantic-release\` analyses conventional commits, bumps version,
creates GitHub Release draft + tag |
| \`build.yml\` | Auto on tag push from release.yml | mac / windows /
linux parallel builds, artefacts attached to the GitHub Release |

## Pre-merge verification (local, this branch)

- ✅ \`pnpm -r typecheck\` — green across 18 workspace projects
- ✅ \`pnpm test\` — 17/17 packages
- ✅ Merge resolved: take develop versions for 19 conflicted
package.jsons (develop has equal or newer deps than main's dependabot
bumps)

## Post-merge action items (operator)

1. **Deploy API to staging first** (smoke test):
   \`\`\`
   gh workflow run deploy-api.yml -f environment=staging
   \`\`\`
2. Confirm staging API responds correctly (subscription endpoint with
new \`LICENSE_SIGNING_PRIVATE_KEY\` secret already set in CF).
3. Merge this PR → auto-deploys API to production.
4. Trigger Release workflow: GitHub → Actions → Release → "Run workflow"
→ main.
5. Watch Build workflow for mac/win/linux completion.
6. Confirm the release un-drafts itself.

## Known risks / follow-ups

- **Pre-existing Vercel preview failure for \`apps/web\`** — marketing
site, scheduled to be extracted to its own repo (P3 in the roadmap).
- **\`SUBSCRIPTION_PUBLIC_KEY\` is dev-grade** — generated in a Claude
session. Before the licensing server emits envelopes for real paid
users, rotate the keypair from a trusted machine and ship a follow-up
release.
- **Branch protection should require CodeRabbit completion before
automerge** — added to the roadmap as a process item; this very PR was
BLOCKED correctly because of that policy gap being closed.

🤖 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>
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