Skip to content

fix(desktop): bound backend shutdown wait during quit - #7599

Merged
maria-rcks merged 2 commits into
pingdotgg:mainfrom
ishaanko:fix/desktop-quit-shutdown-timeout
Sep 12, 2026
Merged

fix(desktop): bound backend shutdown wait during quit#7599
maria-rcks merged 2 commits into
pingdotgg:mainfrom
ishaanko:fix/desktop-quit-shutdown-timeout

Conversation

@ishaanko

@ishaanko ishaanko commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Problem

The quit-time finalizer stopped every backend instance in the pool with no timeout. If any one backend didn't exit cleanly, the wait never ended, and the app never called app.quit(). Since PR #6562 now destroys the window before this wait, the hang became invisible: holding Cmd+Q looked like it did nothing, and the only way out was a force quit.

Fixes #7598

Fix

Bound the wait to 5 seconds, matching the identical pattern already used for this same operation in apps/desktop/src/updates/DesktopUpdates.ts. On timeout, the close keeps running in the background instead of blocking quit, so a slow-but-healthy shutdown still finishes cleanly, and a genuinely hung one no longer blocks the app from exiting.

Extracted the finalizer's logic into an exported stopAllPoolInstances function so it has a real seam to test against.

Testing

  • New test in DesktopBackendManager.test.ts calls the real stopAllPoolInstances export with a mocked pool whose instances hang past the timeout, and asserts the call still completes on time. Verified by hand that removing the timeout argument makes this test hang, confirming it actually catches a regression.
  • vp test run apps/desktop/src/backend/DesktopBackendManager.test.ts: 26 passed
  • vp run --filter @t3tools/desktop typecheck: clean

Model: Claude Sonnet 5. Harness: Claude Code.


Note

Medium Risk
Changes desktop quit/shutdown behavior for all pooled backends; bounded wait reduces hang risk but timed-out teardown may still run in the background.

Overview
Fixes quit hangs when a backend pool instance never finishes teardown (e.g. after the window is destroyed first), so Cmd+Q could appear to do nothing until force quit.

Quit finalizer now goes through exported stopAllPoolInstances, which stops every pooled backend concurrently with a 5s timeout per instance.stop—same pattern as DesktopUpdates.ts. After the budget, quit keeps moving instead of waiting forever; slow but healthy shutdown can still finish in the background.

Adds a regression test that calls the real stopAllPoolInstances with a mocked pool whose instances hang past the timeout and asserts the call completes on time.

Reviewed by Cursor Bugbot for commit 6187110. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Bound backend shutdown wait to 5 seconds per instance during desktop app quit

  • Extracts a new stopAllPoolInstances function in DesktopApp.ts that stops each instance from DesktopBackendPool with a 5-second timeout, running stops concurrently.
  • Replaces the inline teardown loop in scopedProgram's finalizer with a call to stopAllPoolInstances, preventing the app from hanging indefinitely on quit if a backend fails to stop.
  • Adds a test in DesktopBackendManager.test.ts that simulates hanging teardown and verifies the effect completes after the timeout.

Macroscope summarized 6187110.

Summary by CodeRabbit

  • Bug Fixes
    • Improved application shutdown reliability when backend services fail to stop promptly.
    • The app now proceeds with shutdown after a five-second limit instead of waiting indefinitely.
    • Backend cleanup continues in the background and completes when services become available again.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: d60a55fd-9e76-418f-bbf1-a60b60162abc

📥 Commits

Reviewing files that changed from the base of the PR and between c52b8d9 and 44ad0b1.

📒 Files selected for processing (2)
  • apps/desktop/src/app/DesktopApp.ts
  • apps/desktop/src/backend/DesktopBackendManager.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The desktop app now exposes a shared helper that stops all backend pool instances with five-second timeouts. The shutdown finalizer uses this helper. A test verifies that hung backend teardown does not block quitting.

Changes

Desktop shutdown

Layer / File(s) Summary
Bounded backend stop flow
apps/desktop/src/app/DesktopApp.ts
The app lists all backend pool instances and stops them concurrently with a five-second timeout. The shutdown finalizer calls the shared helper.
Shutdown timeout validation
apps/desktop/src/backend/DesktopBackendManager.test.ts
The test verifies that the quit finalizer returns after five seconds while backend teardown remains blocked, then completes teardown after release.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 44ad0

Backend shutdown can no longer indefinitely block desktop quit, including when teardown hangs.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: bounding backend shutdown time during desktop application quit.
Description check ✅ Passed The description explains the problem, fix, testing, scope, and risk. It does not use the template headings exactly and omits the checklist, but it provides the required information in equivalent secti…
Linked Issues check ✅ Passed The changes address issue #7598 by applying a 5-second timeout to each backend stop, allowing quit to continue when teardown hangs, updating the quit finalizer, and adding regression coverage.
Out of Scope Changes check ✅ Passed The changes are limited to desktop backend shutdown behavior and its regression test. They directly support the linked issue and stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 19, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 6187110

Macroscope's review found this PR approvable — Straightforward bug fix that adds a 5-second timeout to backend shutdown during quit, preventing indefinite hangs. The change extracts existing logic into a named function, adds a timeout parameter, and includes a comprehensive test. Limited scope with clear defensive purpose.

You can add or adjust custom eligibility rules. Learn more.

@shivamhwp

Copy link
Copy Markdown
Collaborator

Note: GPT-6 on behalf of shivam (@shivamhwp).

Please make the shutdown regression wait for both backend finalizers to start, then wait for both to finish after releasing allowTeardown. The shared teardownStarted Deferred only confirms one backend reached teardown, and advancing TestClock by 100 ms does not establish that either finalizer finished. Use per-instance Deferreds or queue notifications for those milestones.

Please also invoke stopAllPoolInstances from an Effect.addFinalizer scope in the regression. That matches the quit path's finalizer context instead of exercising only an ordinary interruptible call.

ishaanko and others added 2 commits September 10, 2026 16:51
The quit-time finalizer stopped every backend instance with no timeout,
so a backend that didn't exit cleanly hung the wait forever. Since a
recent change now destroys the window before this wait, the hang was
invisible: holding Cmd+Q looked like it did nothing, requiring a force
quit. Apply the same 5s timeout already used for this identical
operation in DesktopUpdates.ts, so quit always makes progress.
The regression test used one shared Deferred for "teardown started", so
it only proved one backend reached its finalizer, and it inferred
completion from a TestClock advance instead of observing it. Report
start and finish per instance through queues and wait for both.

Also run stopAllPoolInstances from an Effect.addFinalizer scope so the
test exercises the same finalizer context as the quit path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ishaanko

Copy link
Copy Markdown
Contributor Author

Thanks, both points are addressed in 44ad0b1.

Per-instance milestones. Each backend's process-scope finalizer now offers its instance name to a teardownStarted queue when it begins and to a teardownFinished queue when it ends. The test takes two entries from teardownStarted before it advances the clock, so both backends are proven to have reached teardown. After the quit finalizer returns, it asserts teardownFinished is still empty, releases allowTeardown, and then takes two entries from teardownFinished. No clock advance stands in for completion any more.

Finalizer context. The test now registers stopAllPoolInstances with Effect.addFinalizer inside an Effect.scoped block and lets the scope close run it, which matches how DesktopApp invokes it on quit.

I also rebased onto current main, since the old branch no longer shared history with it. Verified by hand that removing the timeout argument makes the test hang, so it still catches the regression.

Model: Claude Fable 5.1. Harness: Claude Code.

@ishaanko
ishaanko force-pushed the fix/desktop-quit-shutdown-timeout branch from 6187110 to 44ad0b1 Compare September 11, 2026 00:10
@maria-rcks
maria-rcks merged commit 8ddd9f7 into pingdotgg:main Sep 12, 2026
22 checks passed
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 13, 2026
## What's Changed
* fix(mobile): render photo library picks to a bounded JPEG off the JS thread by @Nelglor in pingdotgg/t3code#11440
* fix(desktop): keep the native preview User-Agent so Turnstile passes by @akriaueno in pingdotgg/t3code#7110
* fix(chat): keep user input outside collapsed work by @maria-rcks in pingdotgg/t3code#11363
* fix(web): preserve preview focus on window return by @Lucenx9 in pingdotgg/t3code#11444
* fix(web): complete thread status icons and keep input threads prominent by @maria-rcks in pingdotgg/t3code#11461
* feat(web): tint image chips with their average color by @maria-rcks in pingdotgg/t3code#11468
* fix(web): move viewer controls outside media and restore arrow navigation by @maria-rcks in pingdotgg/t3code#11470
* fix(web): tighten sidebar search and footer spacing by @maria-rcks in pingdotgg/t3code#11466
* feat(web): subagent spawns render as an expandable work row by @maria-rcks in pingdotgg/t3code#11433
* fix(web): keep subagent rows visible under folded turns by @maria-rcks in pingdotgg/t3code#11474
* fix(usage): make unavailable account limits more visible by @dominic-r in pingdotgg/t3code#10601
* fix(desktop): bound backend shutdown wait during quit by @ishaanko in pingdotgg/t3code#7599
* feat(web): choose the default diff file state by @maria-rcks in pingdotgg/t3code#11484
* feat(composer): fold large pastes into text attachments by @chrisdeeming in pingdotgg/t3code#11442
* feat(web): expose each chat message as a heading for screen readers by @Leos-Khai in pingdotgg/t3code#11199
* fix(usage): respect provider account homes by @maria-rcks in pingdotgg/t3code#11485

## New Contributors
* @Nelglor made their first contribution in pingdotgg/t3code#11440
* @akriaueno made their first contribution in pingdotgg/t3code#7110
* @Leos-Khai made their first contribution in pingdotgg/t3code#11199

**Full Changelog**: pingdotgg/t3code@v0.0.41-nightly.20260912.1612...v0.0.41-nightly.20260913.1625

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.41-nightly.20260913.1625
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 13, 2026
Brings opt-in thread notifications and sounds, pastes folded into text
attachments, saved environments switched off rather than removed,
subagent spawns as expandable work rows, per-message screen-reader
headings, provider account homes in usage, and the mobile launch crash
fix for threads with a PR stack.

Fork features kept alongside: Computer Use and Computer History IPC,
the `#` cross-thread reference chip, environment rename on web and
mobile, the Goals composer wiring, inline visualizations, and the
skills settings entry (whose ordering a test asserts).

Notable resolutions:

- Desktop shutdown keeps the fork's Computer History daemon stop and
  delegates the pool teardown to upstream's new `stopAllPoolInstances`,
  so its bounded wait (pingdotgg#7599) still applies.
- Upstream's account-home work (pingdotgg#11485) folded into the fork's
  `usageHomes.ts` rather than bolted beside it; grok now resolves a
  home per instance instead of one host-level `GROK_HOME`.
- `enabled` became required on `ConnectionCatalogEntry` (pingdotgg#11478), which
  four auto-merged test fixtures did not set.

Two test-isolation bugs surfaced while verifying, both fixed here:

- The usage scan resolved OpenCode's data dir from `process.env` while
  every other provider read the injected host environment, so the suite
  scanned the developer's real `~/.local/share/opencode` and its source
  count depended on whose machine ran it.
- Its fixture root was left uncanonicalised, so on macOS every path
  assertion compared `/var/...` against the `/private/var/...` the scan
  resolves. That mismatch also stalled one test for its full 120s
  timeout; the file now runs in 2s instead of 122s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aorwall added a commit to aorwall/t3code that referenced this pull request Sep 13, 2026
Merges `upstream/main` at `0c5771d60` into the fork, from merge base
`e81606494` — 32 upstream commits.

The range is mostly client polish, plus two structural changes that
mattered
here: upstream extracted the sidebar header into a new component
(`SidebarThreadHeader.tsx`, pingdotgg#11315), which is where two fork gates had
to be
re-homed, and upstream added a `context` field to orchestration messages
at the
exact anchor the fork's `origin` field sits on, which is four of the
eight
conflicts.

## Merge stats

- Landed (`HEAD^1..HEAD`): 489 files, 36117+/5930−
- Upstream range (base..`HEAD^2`): 484 files, 35784+/5824−
- Fork delta (`HEAD^2..HEAD`): 767 files, 78724+/2528−

The two file lists reconcile exactly. The 5 extra landed files are all
fork-owned and none of them is upstream work:
`apps/web/src/fork/SidebarThreadFilter.tsx`
(one className, described below), `docs/fork/inventory.json`,
`docs/fork/gaps.md`, `docs/fork/upstream-merge-log.md`, and
`.agents/skills/fork-upstream-merge/scripts/unsupported-methods.mjs`.
Nothing in
the upstream range failed to land.

## Conflicts

All 8 were resolved by the verdict `preflight.mjs` printed. No `decide`
conflict
was left unresolved.

- `projector.ts`, `orchestration.ts`, `threadReducer.ts`,
`MessagesTimeline.tsx`
— `converged — message-origin-upstream-files`, and all the same
conflict:
upstream appended where the fork already appends. Both sides kept, twice
per
file for the first three. `duplicate-adds.mjs` confirms no line was
taken
  twice.
- `Sidebar.tsx` — `converged — thread-visibility-upstream-files`. Took
upstream
whole; its `SidebarThreadFilter` import was left unused by the
extraction and
  was removed.
- `SettingsSidebarNav.tsx` — unlisted. Kept the fork's
`settingsPathEnabled`
filter over the personal nav items and took upstream's new active-state
rule
(`/settings/general` stays active on `/settings/open-source-licenses`).
- `ChatComposer.tsx` — unlisted, so `decide, then add an entry`. Both
fork deltas
  survived and the entry is now written; see below.
- `routeTree.gen.ts` — generated; regenerated with
`regen-route-tree.mjs` after
  the install.

`pnpm-lock.yaml` auto-merged rather than conflicting, so it was reset to
`upstream/main` and the fork edges re-derived with `vp i`. The remaining
diff
against upstream is exactly the `@t3tools/moatless-api` workspace link,
`mermaid ^11.17.2`, and one alchemy peer hash.

Two findings worth naming here:

- **A fork gate's host file was replaced by a file upstream had not
written yet.**
  pingdotgg#11315 extracted the whole sidebar header into
`apps/web/src/components/sidebar/SidebarThreadHeader.tsx`. Both fork
deltas
were re-applied there additively — the `FEATURES.projectManagement` gate
on
New project, and `<SidebarThreadFilter />` as a third child of
upstream's new
segmented icon well. No props threaded, no state added, no upstream JSX
re-indented. The one edit outside that file is
`SidebarThreadFilter.tsx`'s
  trigger className, now `size-7` so it matches upstream's own
  `SidebarHeaderIconButton` in the well it now sits in.
- **The unsupported-method derivation could not read the backend, and
that was
the script's fault, not a finding.** `unsupported-methods` exited 2 with
"could
not read the backend dispatch". The Moatless backend moved its dispatch
a
second time: `crates/t3code/src/rpc/dispatch.rs` is now a module stub
over an
`rpc/dispatch/` directory whose `routing.rs` holds the arms and whose
siblings
hold the handler bodies. `BACKEND_APIS` now names the directory and the
script
concatenates every `.rs` file in it — pointing it at `routing.rs` alone
would
have read the arms and lost the handlers, and `refusesInside` only
follows
calls it can find in the same source, so every conditional refusal would
have
  come back as a false DROP.

## Inventory

- `moatless-admin-pages` was stale: it still listed the two Workspaces
admin
routes that the 2026-09-12 commit folded into the project settings page.
Re-pointed to the five surfaces that remain, and the untracked delta
that move
  left behind is now its own entry, `project-workspace-settings`.
- `chat-surface-gates` gained
`apps/web/src/components/chat/ChatComposer.tsx`
with a guard on `FEATURES.accessMode`, plus a `chat-composer-gates` path
policy
so the next merge gets a cached verdict instead of the same decision.
The two
deltas there are the runtime-mode picker lifted into a
`runtimeModePicker`
  const behind the flag, and `phase === "running"` left out of
  `collapsedComposerPrimaryActionDisabled`.
- `inventory-check.mjs` is clean.

## Unsupported methods

0 ADD, 0 DROP, 2 KEEP (`git.preparePullRequestThread`, `vcs.switchRef`),
5 known
exceptions still firing, no stale ones. `packages/contracts/src/rpc.ts`
is
unchanged: the range's one unsupported-surface change is upstream's
Cursor
`--classic` launcher fix, which lands on a method already refused.

## Feature classification

### Usable as-is

Client-side work the fork can expose with no Moatless backend or
deployment
change. 28 of the 32 commits.

- Open-source license notices page (pingdotgg#8962) — new
`/settings/open-source-licenses`
route; upstream also made `/settings/general` stay active while it is
open.
- Client perf: fewer repeated sorts and date formats (pingdotgg#11019).
- Inline file previews and attachment chips across surfaces (pingdotgg#11265) —
rides
  `attachments.createUploadUrl` and `assets.createUrl`, both dispatched.
- Subagent spawns as an expandable work row (pingdotgg#11433) and those rows kept
visible
under folded turns (pingdotgg#11474) — derived from the orchestration event
stream the
  backend already serves.
- Opt-in thread notifications and sounds (pingdotgg#11481) — client settings,
persisted
  through the `server.getSettings` read the backend serves.
- Large pastes folded into text attachments (pingdotgg#11442); user input kept
outside
collapsed work (pingdotgg#11363); each chat message exposed as a heading for
screen
  readers (pingdotgg#11199); the default diff file state (pingdotgg#11484).
- Sidebar project scope folded into the search row (pingdotgg#11315); thread
status icons
completed and input threads kept prominent (pingdotgg#11461); sidebar search and
footer
  spacing (pingdotgg#11466); draft row heights matched to thread rows (pingdotgg#11512).
- Image chips tinted with their average colour (pingdotgg#11468); viewer controls
moved
outside the media with arrow navigation restored (pingdotgg#11470); snapshot
preview size
preserved in sent messages (pingdotgg#11429); preview focus preserved on window
return
  (pingdotgg#11444).
- Unavailable account limits made more visible (pingdotgg#10601) — web-only; the
backend
  dispatches `server.getUsageSummary`.
- Saved environments switched off instead of removed (pingdotgg#11478) — entirely
client-side (connection catalog and registry). This build runs one
environment
and gates the Connections settings page, so nothing on screen changes;
the
  catalog behaviour carries.
- Desktop and mobile: long offscreen text in SnapShots (pingdotgg#11250), native
preview
User-Agent kept for Turnstile (pingdotgg#7110), bounded backend shutdown wait on
quit
(pingdotgg#7599), expo-audio pinned (pingdotgg#11426), photo library picks rendered to a
bounded
JPEG off the JS thread (pingdotgg#11440), launch crash with a PR stack (pingdotgg#11486),
the
  shared-content alert after sending (pingdotgg#11487).
- Repository hygiene: `.pnpm-store/v11` deleted.

### Unsupported in Moatless / needs implementation

- **Cursor links open in classic IDE mode (pingdotgg#11498).** Upstream gave
Cursor
`baseArgs: ["--classic"]` in `packages/contracts/src/editor.ts` so a
file open
  targets the IDE rather than its Agents Window, and tested it in
  `apps/server/src/process/externalLauncher.ts`. The method behind it,
`shell.openInEditor`, is not dispatched — the browser is not on the
machine the
workspace is on — so this lands in the contract and in `apps/server` and
changes nothing here. Recorded in `docs/fork/gaps.md` under _Opening in
an
external editor_, whose standing conclusion is that the surface is a
candidate
  for deletion rather than for serving.

### Backend behavior to consider reproducing in Moatless

All three are recorded in `docs/fork/gaps.md` under _Runtime fixes
upstream made
to its own server_. Nothing in this repository holds them open; they are
Moatless-side work.

- **Listing pull requests should read only the projects asked about
(pingdotgg#11299).**
`listWorkspaceProjects` fetched the whole shell snapshot and filtered
it; it now
  asks the projection for the one project, or for the listed ids
  (`apps/server/src/pullRequest/PullRequestService.ts`,
  `persistence/Layers/ProjectionSnapshotQuery.ts`). Moatless dispatches
`pullRequests.summary`, so the same cost lands on it as soon as a
summary is
  derived from a list.
- **Usage should read each provider account's own history directory
(pingdotgg#11485).**
Upstream resolves an account's home from its home setting or its
`CODEX_HOME` /
`CLAUDE_CONFIG_DIR` / `GROK_HOME` variable, counts disabled accounts,
and
de-duplicates accounts sharing a directory
(`apps/server/src/usage/UsageService.ts`).
Moatless serves `server.getUsageSummary` itself, so an account with a
custom
home reports zero there — or double — until it resolves homes the same
way.
- **Forgejo and Gitea remotes should be first-class source control
(pingdotgg#11436).**
Upstream recognises both hosts and drives them with the `fj` and `tea`
CLIs
  across remote identity, PR creation and PR sync (`git/GitManager.ts`,
  `project/RepositoryIdentityResolver.ts`,
`orchestration/PullRequestSyncReactor.ts`). Moatless owns git and pull
requests,
so a Forgejo or Gitea project is an unrecognised host there regardless
of what
  the client can render.

## Verification

`verify.mjs` is green on seven of eight checks: `duplicate-adds` (none
across 34
files both sides changed), `tripwires` (3 deleted surfaces intact,
exactly the 5
known re-deletions, 3 allowed workflows), `resolution-check` (16
fork-delta paths
still differ from upstream, 17 carry upstream's change, 17
theirs-verbatim
byte-identical, 18 unlisted), `unsupported-methods`, `fmt:check`,
`lint`,
`typecheck`.

`test` is red on one file, and it is the standing environmental failure
rather
than a merge regression:

- `@t3tools/desktop` → `scripts/browser-secret-native.test.mjs > bundled
libsecret
helper` fails with `Package 'libsecret-1' not found` from `pkg-config`.
1 file
  of 105; the rest of the package is 1341 tests passed. The test file is
byte-identical to upstream, arrived on the fork before this merge, and
the
sandbox image ships neither `libsecret-1` nor its pkg-config file. There
is no
  root in the sandbox, so it cannot be installed here. Recorded in
`docs/fork/gaps.md` under _The desktop suite needs libsecret, which the
sandbox
  does not have_.

Four packages did not finish under `vp run -r test` and were each run
alone
again, all green: `@t3tools/mobile` (165 files, 1528 tests), `t3` (317
files,
4528 tests), `@t3tools/web` (412 files, 5205 tests), `t3code-relay` (30
files,
284 tests).

The owned-concern sweep over newly added upstream files found no keyword
hits, so
no `concerns` entry was needed.

**CI caught one thing no local check runs.** `Build & push moatless-t3`
failed on
the first push: upstream's new `t3code:third-party-licenses` plugin
(pingdotgg#8962) runs
in `generateBundle` and refuses any bundled package whose license it
cannot
resolve, and three packages reach the web bundle only through the fork's
own
`mermaid` edge — `khroma` via mermaid, `fastdom` and `strictdom` via
cytoscape
under it — so upstream's config has never carried overrides for them.
Fixed with
three `packageOverrides` entries: `khroma` needed a `license: "MIT"`
declaration
only, since it ships its own `license` file, and `fastdom` and
`strictdom` needed
a `generatedNotice` each, since both declare MIT and ship no notice
file.
Verified with the build itself — all three now appear in
`apps/web/dist/third-party-licenses.json` with a license and a notice,
and the
workflow is green. The delta is held by the `mermaid-diagrams` inventory
entry
plus a `third-party-licenses-config` path policy, and the reason it
escaped
`verify.mjs` — which has no build step at all — is now
`docs/fork/gaps.md`, _Nothing builds the web app before a merge is
pushed_.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@ishaanko
ishaanko deleted the fix/desktop-quit-shutdown-timeout branch September 13, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Holding Cmd+Q can silently hang the app instead of quitting

3 participants