Refactor(boundaries): define portable client and server interfaces - #789
Merged
Merged
Conversation
Desktop, web, and mobile currently depend on private renderer internals and source checkouts, while server builds depend on the frontend build tree. This change introduces explicit public editor APIs, portable packages, and verified browser artifacts so each host can build against a deliberate dependency boundary. ### Review groups 1. **Contracts and domain:** move portable types into the bridge contract, preserve compatibility exports, and share exact-byte behavior fixtures. 2. **Editor APIs and lifecycle:** expose navigation, editor commands, attachments, notes, folders, databases, tasks, settings, and immutable host snapshots. Coordinate saves, file mutations, workspace transitions, and native relocation/rollback. 3. **Package distribution:** build immutable core/contract/domain archives, preserve singleton React/CodeMirror dependencies, and verify external consumers on Vite 6 and 8. 4. **Server and viewer artifacts:** support Go-only API builds and verified embedded web imports; restore a maintained read-only public viewer with safe Markdown fallback and restricted interactive plots. 5. **Extraction preparation:** provide Go repository, Docker, Nix, and draft-release templates for the later `ZenNotes/znserver` cutover. The current server source and distribution owner remain active until that cutover. This branch starts from current `main` after v2.50.4, including its verified AUR/Homebrew/Nix metadata. The asset-reference fix is reconciled with the workspace reservation so pending edits and vault switches cannot interrupt a rename/move rewrite.
This was referenced Sep 16, 2026
Closed
… CodeQL The standalone package check installed the packed archives with npm's offline mode, which only works when the npm cache already holds registry metadata for every dependency range. npm ci caches tarballs, never that metadata, so every fresh CI runner failed to resolve lz-string. The check now prefers the cache but allows the metadata fetch. On Windows the packers wrote include and exclude globs with backslashes, and tsc treats those as patterns that match nothing. Generated tsconfig paths now use forward slashes on every platform. Behind that failure waited another: Windows checkouts convert the contract fixtures to CRLF, and the TypeScript, Go server, and TUI consumers hash those bytes against recorded provenance. The fixture directories now check out with LF everywhere. CodeQL flagged eight items in the large diff. The wikilink resolvers trim slashes with a linear scan instead of a backtracking regex, the browser harness only dispatches CDP events to registered listeners, and store.ts drops four unused imports and an unreferenced helper. Validation: the shared-package check fails on an empty npm cache before and passes after; the Windows glob is reproduced with path.win32; the CRLF hash mismatch is reproduced arithmetically against the pinned digests; root typecheck, every workspace suite with the turbo cache bypassed, the isolated package check, and the Chrome harness pass with these changes.
…ture The regression test added with the CodeQL cleanup built its own two-note array without the title and folder fields that NoteRef requires, so the app-core typecheck failed on every platform once the earlier steps passed. Vitest ran the test without complaint because it does not typecheck. Reuse the module-level fixture, which already holds both notes the test needs.
The installed-editor check ran on a GitHub runner for the first time and stopped at "editor: timed out" with nothing else in the log. The harness already collected page errors, failed requests, helper output, screenshots and DOM dumps, but wrote them into a throwaway temporary directory and never printed them, so the failure was unreadable from CI. On failure the harness now prints the collected errors, failed requests, the tail of the server, preview and Chrome output, and a compact page state. A small collector copies the screenshots and JSON dumps out of the consumer directory so the Ubuntu job can upload them as an artifact whether the step passes or fails. The first note open pulls the editor, store and Markdown chunks on a cold runner, so it now gets the same sixty seconds the lazy Mermaid and Typst renders already had. A real rendering failure still fails, with reasons. Validation: the harness still passes locally against real Chrome; the collector gathers fifteen files from a previous local run.
…nd match Windows paths in the rollback test The Ubuntu runner reached the sidebar before the workspace finished restoring, so the harness's first openNote landed during the transition and the public navigation guard dropped it by design: no error, no editor, and sixty seconds later the page was still Home with the note index loaded. The harness now waits for the shell's workspaceRestored signal, the same readiness the later host-command section already required, before opening its first note. The evidence upload and page dump from the previous run are what made this visible. The Empty Trash rollback test refuses the comment-tree move by matching a rename source that ends in .zennotes/comments/trash. Windows joins that path with backslashes, so the refusal never fired, the transaction succeeded, and the assertion that it rejects failed on that runner alone. Compare the normalized path so the refusal fires on every platform. Validation: the browser harness passes locally against real Chrome and in a Debian container with Chromium; the vault suite passes locally; macOS and Linux arm64 already passed the full build on the previous run.
… the delayed cache write before tests remove a vault The previous run showed the same restoration race at the harness's next navigation: after loading the host page it opened a note while the workspace was still restoring, and the public navigation guard dropped the call. Every scripted navigation now goes through one readiness helper that waits for the shell's workspaceRestored signal before it opens a note. On Windows the vault package failed in cleanup rather than in a test: ListNotes schedules a note-meta cache snapshot one second later, and the slow runner reached t.TempDir removal while that goroutine was creating files, so Windows refused to delete the directory. The same race exists on main; it only surfaced here because the runner happened to line up with the delay. The vault now counts its delayed writers and exposes Close to drain them, and the package's tests keep the writer off through TestMain so no test can race its own cleanup. A test that opts back in drains with Close. Validation: the browser harness passes locally against real Chrome; go vet and the full server suite pass in the branch worktree.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Desktop, web, and mobile currently depend on private renderer internals and source checkouts, while server builds depend on the frontend build tree. This change introduces explicit public editor APIs, portable packages, and verified browser artifacts so each host can build against a deliberate dependency boundary.
Review groups
ZenNotes/znservercutover. The current server source and distribution owner remain active until that cutover.This branch starts from current
mainafter v2.50.4, including its verified AUR/Homebrew/Nix metadata. The asset-reference fix is reconciled with the workspace reservation so pending edits and vault switches cannot interrupt a rename/move rewrite.Validation
node_modules.Draft status and dependencies
Companion consumer PRs are prepared separately. Current mobile/viewer pins are validated local candidates with honest dirty-source provenance. Clean artifact publication and consumer repinning, account-backed Cloud/iCloud staging, destination CI, and server channel cutover remain explicit gates. This PR does not deploy, publish artifacts, remove the old Go source, or implement Cloud browser login.
See
docs/specs/ecosystem-boundaries-and-repository-plan.md,docs/boundary-release-cutover.md, anddocs/v2.50.4-boundary-integration.mdfor ownership, compatibility, evidence, and rollout order.Related boundary PRs