Skip to content

Align the macOS File menu with the in-app project switcher. The File…#372

Closed
inkeep-oss-sync[bot] wants to merge 5 commits into
mainfrom
copybara/sync
Closed

Align the macOS File menu with the in-app project switcher. The File…#372
inkeep-oss-sync[bot] wants to merge 5 commits into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

Align the macOS File menu with the in-app project switcher. The File menu's project actions now sit together directly under "New from template…" and read in the same order as the bottom-left switcher: Recent project, New project, Switch project, Open folder. "Create new project…" is renamed "New project…" and the recents submenu is renamed "Recent project" to match the switcher's labels. The switcher's own action order is updated to the same sequence (New project, Switch project, Open folder) so both surfaces are consistent. Wiring, accelerators, and behavior are unchanged.

Fix links({ kind: "dead" }) falsely reporting freshly-written docs as dead. The dead-link check decided a target existed only from the file-watcher's file index, which lags behind in-session writes, so a doc the link graph had just registered a backlink for could still be flagged dead until a server restart. Dead-link resolution now also treats any doc the graph already holds as a live node (its body has been indexed) as a valid target, so a newly-written doc is a valid link target immediately — without changing how genuinely-missing targets are reported.

mike-inkeep and others added 5 commits June 29, 2026 14:33
…ift toast (#2242)

* fix(desktop): shut down stale server on app update to stop version-drift toast

The desktop spawns its CRDT server detached so it survives app-quit
(precedent #1261). But app updates install via app-quit too — either the
silent `autoInstallOnAppQuit` path or a drag-replace — and only the
"Relaunch now" button ran `prepareForRelaunch` → `stopAllOwnedServers`.
So on the common update paths the old-version server kept running, the
relaunched app attached to it, read the older version off `server.lock`,
and showed the "this project is running an older version… restart it"
toast on nearly every update.

Hook the teardown to `before-quit-for-update` — the native autoUpdater
signal that fires on BOTH install paths and ONLY on an update install,
never a plain quit. New `WindowManager.signalStopAllOwnedServers()` sends
a synchronous best-effort SIGTERM to every detached server this desktop
spawned (the "Relaunch now" path already drained them, so it no-ops
there). The event can't hold the quit open, but the server's own SIGTERM
handler flushes pending writes and releases its lock in ~25ms (measured) —
far inside the multi-second reinstall+relaunch window — so the new app
spawns fresh instead of re-attaching. A normal quit never fires the event,
so it still leaves the server running, as designed.

The signalling loop is extracted as the pure `signalDetachedServerStop`
helper with unit coverage (normal kill, ESRCH already-gone, non-ESRCH
failure keeps going, empty no-op), since `WindowManager`'s teardown
methods have no unit harness.

Claude-Session: https://claude.ai/code/session_01Se7d83STuBxsEbdmU4pWD2

* fix(desktop): also reap ephemeral servers on update + dedup utility-fork loop

Address both reviewer "Consider" findings on #2242:

1. Ephemeral single-file (`ok <file>`) session servers are tracked on
   `ctx.ephemeral` (keyed by file path, not project root) and so weren't
   reaped by the new before-quit-for-update teardown — they'd orphan on the
   silent `autoInstallOnAppQuit` path (process + temp dir until reboot), the
   way the async sibling `stopAllOwnedServers` already prevents. Signal their
   pids too. Temp-dir removal is the async half this best-effort path can't
   do; the orphaned process (which holds the bundle binary) is what matters
   and dies on the SIGTERM.

2. The utility-fork SIGKILL loop was duplicated near-verbatim across both
   teardown methods. Extract the shared pure `signalStopOwnedUtilityForks`
   helper and call it from both, so the predicate + error handling can't
   drift — and the branch gains unit coverage (3 new tests).

Claude-Session: https://claude.ai/code/session_01Se7d83STuBxsEbdmU4pWD2

* test(desktop): cover signalStopAllOwnedServers orchestrator end-to-end

Re-review follow-up: the extracted helpers were unit-tested but the
orchestrator entry point `signalStopAllOwnedServers()` (wired to
before-quit-for-update) had no direct coverage of its composition —
collecting detached pids from `spawnedDetachedPids` + ephemeral pids from
`windowsByPath`, draining the map, merging. Add a test through the existing
ephemeral harness: seed a detached pid + an open ephemeral session, assert
both receive SIGTERM, and assert a second call doesn't re-signal the
detached pid (map drained). Guards against a future field-rename in the
collection chain that would pass the helper tests but break orchestration.

Claude-Session: https://claude.ai/code/session_01Se7d83STuBxsEbdmU4pWD2

---------

GitOrigin-RevId: ab2b0fe9c2a6d159d9b3f3ccb3183fc170ae952d
…251)

pickLatestBetaDmgUrl returned the first -beta.N release in GitHub's
"List releases" array order, trusting it to be newest-first. It isn't:
the API has been observed returning an older beta ahead of newer ones,
and the bug surfaced the moment betas crossed from single- to
double-digit (beta.9 was served while beta.10-13 existed), stalling
/download/beta and the /updates/beta auto-update feed on a stale build.

Pick the newest beta by numeric (major, minor, patch, beta) rank instead
of position. Fixes both surfaces, since the updates route derives its tag
from the same resolver. Adds regression tests for the older-first array
order and the beta.9-vs-beta.10 lexical trap.

GitOrigin-RevId: 353c018fe8860fd1ad02051f36989f6d2c97715c
GitOrigin-RevId: aa53d68b0739a9b4e289a10c46ab732544579bfa
* feat: align macOS File menu with the in-app project switcher

Group the File menu's project actions (Recent project, New project,
Switch project, Open folder) directly under "New from template..." in
the same order as the bottom-left ProjectSwitcher, and update the
switcher's action order to match. Rename "Create new project..." to
"New project..." and "Open recent" to "Recent project" so both
surfaces read identically. Wiring, accelerators, and behavior unchanged.

* chore: centralize project menu labels, refresh stale references

Address PR review feedback:
- Fold the dual-surface 'New project' / 'Open folder' labels into MENU_LABELS
  so menu-label-parity.test.ts guards menu<->switcher wording drift (matching
  the existing shared-label pattern); normalize their ellipsis to the same
  escape the sibling labels use.
- Add contiguity assertions to the menu order test so a separator inserted
  mid-section would be caught, not just relative ordering.
- Refresh stale 'Create New Project...' references to the renamed label at the
  onNewProject call sites (index.ts, App.tsx, CreateProjectMenuTrigger,
  NavigatorApp) and in the onNewProject JSDoc.

GitOrigin-RevId: ac7db1562d92e3ba8230ff7b17daa106d33d9e2a
…cs (PRD-7194) (#2248)

* fix(open-knowledge): stop dead-link check flagging freshly-written docs

`links({ kind: "dead" })` reported recently-created docs as dead even
though their target files existed and the backlink edge was already
registered. Dead-link resolution decided existence solely from the
caller-supplied admitted set (the file-watcher's file index), which lags
behind in-session writes. The same backlink index, meanwhile, registers
a new doc as a live forward node the instant `onStoreDocument` runs — so
the graph simultaneously held a backlink FOR the doc and reported it as a
dead link, an internal inconsistency that only cleared on a server
re-index/restart.

`getDeadLinks` now treats a target as existing when it is admitted OR is
a live forward node in the graph (`state.forward.has(target)`). A
genuinely-missing target is never a forward node (only `state.backward`
carries it), so this never hides a real dead link; a freshly-written doc
becomes a valid link target immediately within the same session.

Fixes PRD-7194.

Claude-Session: https://claude.ai/code/session_01HgfN67bZN3AUMkA2sa7dMf

* test(open-knowledge): pin dead-link deletion inverse + document existence contract

Address claude[bot] review on #2248 (both low-risk suggestions):
- Add a companion test asserting that after `deleteDocument` drops a doc
  from `state.forward`, `getDeadLinks` reports it dead again when it's also
  absent from the admitted set — guards against a future refactor that
  retains stale forward entries and silently swallows real dead links.
- Add JSDoc to `getDeadLinks` making the existence contract explicit:
  existence is `admittedDocs ∪ keys(state.forward)`, an additive second
  oracle, not a narrowing of the admitted set.

Claude-Session: https://claude.ai/code/session_01HgfN67bZN3AUMkA2sa7dMf

---------

GitOrigin-RevId: 6834ae8bfb71348f858c05c55abc489b883733fa

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval from agents-private public-mirror-sync (run: https://github.com/inkeep/agents-private/actions/runs/28379621214). Source of truth is the monorepo; direct edits on inkeep/open-knowledge are overwritten on next sync.

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor Author

Auto-closed by public-mirror-sync: a newer sync run is rebuilding copybara/sync with accumulated changes from agents-private. A fresh PR will be opened momentarily.

@inkeep-oss-sync inkeep-oss-sync Bot closed this Jun 29, 2026
@inkeep-oss-sync
inkeep-oss-sync Bot deleted the copybara/sync branch June 29, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants