Trace the root cause to Electron's init order: Ozone platform selection
fires in C++ PreEarlyInitialization() and the chosen platform is
memoized in ui/ozone/platform_selection.cc's g_selected_platform
static. Any app.commandLine.appendSwitch() from main.js runs in
PostEarlyInitialization() — after the memoization, so the write is
correct but the read has already happened. Primary source: electron
PR #48301 diff + Chromium ui/ozone/platform_selection.cc.
This confirms the argv wrapper is structurally the only fix available
from outside the Electron binary. Also documents why the two
lower-friction alternatives don't work:
- ELECTRON_OZONE_PLATFORM_HINT env var was removed as dead code in
Electron 39 (PR #47983).
- Setting XDG_SESSION_TYPE=x11 via snapcraft.yaml environment: would
work for Ozone but would fool IdleTimeHandler, silently breaking
GNOME Wayland idle detection.
Confidence in the mechanism hypothesis is raised from ~40% to ~85%.
Residual unknown: whether GPU child-process CommandLine inheritance
exposes a late appendSwitch to subsystems other than Ozone. Not
verified from source; does not change the shipping recommendation.
Related: issue #7270.
Address findings from code-reviewer + debugger-assistant +
refactor-specialist multi-review on 8e1c47ebc2:
- afterPack: read wrapper source before rename; on writeFile failure,
roll the rename back so a broken intermediate state can't ship a
package with no launcher. Strengthen idempotency check to require a
shebang at binPath (both files alone no longer counts as "installed").
- wrapper: gate the X11 injection on \$SNAP_NAME = "superproductivity",
not just \$SNAP set. An xdg-open from a sibling snap (e.g. Firefox)
leaks \$SNAP into the child env, which previously would have silently
forced XWayland on .deb/.rpm users. Derive BIN_DIR from \$SNAP
directly when we are our snap, avoiding fragile \$0 resolution
through snapd's wrapper chain. Stop argv scanning at -- so positional
args that resemble --ozone-platform aren't misread as a user override.
- app-control: point app.relaunch() execPath at the sibling shell
wrapper instead of the default process.execPath (which is the renamed
ELF and would bypass the flag injection on a relaunched Snap+Wayland
instance).
- Move build/snap-wrapper.sh -> build/linux/snap-wrapper.sh to match
the existing build/linux/ layout convention.
- Update research doc §18 to reflect the tightened predicate and the
relaunch fix.
Field reports on issue #7270 (v18.2.4 / v18.2.5) show that
app.commandLine.appendSwitch('ozone-platform','x11') is not equivalent
to the CLI flag on Snap+Wayland: Chromium's Ozone init dlopens
libEGL/libgbm on the core22 Mesa path before the switch is honored and
segfaults under host-vs-snap Mesa ABI drift, while the same flag on
argv consistently works.
Install an afterPack hook that renames the Electron binary and drops a
shell wrapper at the original name. The wrapper injects the flag into
argv only when \$SNAP is set AND the session is Wayland — non-Snap
targets (AppImage, .deb, .rpm) and X11 sessions pass through untouched.
Matches the mechanism used by snapcrafters/signal-desktop and
snapcrafters/mattermost-desktop.
See docs/research/snap-wayland-gpu-fix-research.md §18 for the full
evidence trail, drawbacks, validation plan, and removal conditions.
Avoids the operations table entirely (sync_devices + users only) so it
returns in milliseconds even when operations grows past 1 GB. Useful when
the standard active-users command is too slow on large prod DBs.
electron-window-state reads isMaximized() on an already-hidden window in
its `closed` handler, which returns false (electron#27838). Patch the
persisted state file in will-quit using our own wasMaximizedBeforeHide
flag, which is kept accurate by the maximize/unmaximize event listeners.
encodeURIComponent leaves ( and ) intact per RFC 3986, but GitHub's
search API treats them as grouping operators that must be percent-encoded,
returning HTTP 422 on queries like "(author:@me OR assignee:@me)".
The in-repo provider was fixed in 960330dd56 but the fix didn't carry
over when GitHub moved to a plugin. Reapply via a small encodeGithubQuery
helper used at both call sites.
The "just 1ms over max clock drift boundary" test captured Date.now()
once in the test and again inside uploadOps. If the second sample
advanced by >=1ms, the op was within the drift window from the
service's perspective, clamping never fired, and toBeLessThan failed
with equal values.
Freeze time with vi.useFakeTimers/setSystemTime so both samples
match, and tighten the assertion to the exact clamped value.
querySelector(`#t-${taskId}`) throws SyntaxError when taskId starts
with `-` or a digit (both valid nanoid outputs). Swap for getElementById
plus container.contains() to keep the scope check.
The showUsage query ran two correlated subqueries over the full
operations table once per user, each calling pg_column_size(payload)
on every row, then sorted all users before LIMIT 20. On a 1.85 GB
operations table with 3.6k users this effectively never returned.
Aggregate per-user size and count in a single CTE pass, then join.
One sequential scan instead of scans × users.
7-reviewer multi-review (6 Claude focus-agents + Codex CLI) over the
cherry-picked PR #7273 diff, plus 5 parallel research agents on Mesa
mechanism, Chromium flag behavior, peer-app field data, core22→core24
cost, and marker-timing semantics.
§17 captures: findings that changed the PR (now shipped in the
previous commit), disagreements worth noting (Codex W-C1 vs. three
independent flag-pair sources), validated decisions (APP_READY signal,
Snap config shape, novelty of the crash-loop mechanism), deferred
scope (pre-flight probe, core24 migration, forensic listener,
TOCTOU hardening), and confidence deltas.
Adds 14 new references (Mesa commit, libgbm ABI instability,
peer-app issues on Ubuntu 25.10, Canonical core24 migration docs,
electron-builder#8548, Obsidian launcher) tying each to the §17
claim it supports.
Defense-in-depth against GPU init failures on Snap/Flatpak Linux where
the main process stays alive but the GPU process crashes at init and
the window never renders. Field data in #7270 (two post-v18.2.4
reports) shows this happens on Ubuntu 24.04+/25.10 regardless of GPU
vendor — the driver is core22 Mesa/libgbm drifted from the host Mesa.
See §12–§17 in docs/research/snap-wayland-gpu-fix-research.md.
Mechanism (electron/gpu-startup-guard.ts):
- Content-based crash marker in userData with {ts, electronVersion}.
Written before app.whenReady() on confined Linux; cleared via
IPC.APP_READY after Angular boot — not ready-to-show, which fires
on blank/broken renderers too.
- Previous-crash detection: marker present AND recent (<5 min) AND
matching Electron version. Staleness bound + version gating drop
systemd-SIGKILL-mid-boot and post-upgrade-residue false-negatives.
- Env overrides SP_DISABLE_GPU=1 / SP_ENABLE_GPU=1 work on all
platforms; auto-detection is Linux+Snap/Flatpak-only.
- Non-ENOENT fs errors logged at warn — a swallowed write-fail
previously meant the guard could re-enter the loop with no
diagnostic trail; a swallowed unlink-fail meant a successful boot
could get permanently stuck in crash-recovery.
Fallback flag bundle (start-app.ts):
--disable-gpu
--disable-software-rasterizer
--ozone-platform=x11
The pair matches Chromium's GPU integration tests' "no GPU process"
invariant; DisplayCompositor handles 2D in the browser process
without spawning a GPU child. app.disableHardwareAcceleration()
alone does NOT — verified against electron/electron#17180/#20702.
The extra --ozone-platform=x11 closes the Chromium 140+
browser-side Wayland/libgbm-dlopen gap on Flatpak (redundant with
the existing Snap X11 widening branch; last flag wins).
Novelty: R3 survey of VS Code, Slack, Insomnia, LosslessCut,
Obsidian flatpak, Firefox snap, and Canonical's gpu-2404-wrapper
found no peer Electron-snap implementing an equivalent reactive
crash-detection + auto-fallback. Prevailing patterns are manual
--ozone-platform=x11, proactive env-sniff, or do-nothing.
Review: §14–§15 multi-agent verification on the original PR #7273;
re-reviewed via 7-agent multi-review (6 Claude focus-agents + Codex
CLI) + 5 research agents here (§17). Live-tested with
SP_DISABLE_GPU=1 on a KDE/X11 dev host — window rendered normally
via DisplayCompositor.
Refs: #7270
The "PR #7273 was closed" paragraph predates the two post-v18.2.4
field reports in §16. Updated to acknowledge the revisit condition
explicitly: reopen only if a real report came in that X11 widening
didn't rescue. §16 documents two such reports (Intel/Ubuntu 24.04 and
AMD/Ubuntu 25.10), both showing #7266's guard firing correctly and
still not rescuing the user. PR #7273's mechanism is no longer
speculative — it rescues the population #7266 provably does not.
Also wrap DISPLAY_COMPOSITOR in backticks on line 846 to prevent
prettier from parsing underscores as an italic span across lines.
Second post-v18.2.4 field report (Ubuntu 25.10 + AMD Raphael) replicates
DerEchteKoschi's failure pattern (Ubuntu 24.04 + Intel Arrow Lake-P):
#7266's guard fires, MESA-LOADER fails on gbm/dri_gbm.so, GPU process
segfaults in a loop regardless of ozone platform. Confirms the tail is
driven by host-Mesa/core22-runtime drift, not GPU vendor or generation.
Changes in §16:
- Intro + Environments: switch to n=2 framing with per-reporter blocks.
- What the log proves: clarify shared pattern; flag nekufa's
`--ozon-platform` typo (short-circuit in start-app.ts:73-75 only
matches `--ozone-platform`, so the log reflects the programmatic path).
- Scope table: drop "Arrow Lake, newer" parenthetical — generic
drift row covers any Ubuntu ≥ 24.04 with core22 mismatch.
- PR #7273 framing + core24 urgency cite n=2 evidence.
- Open question: note nekufa's typo disqualifies from CLI vs
appendSwitch question.
- Actionable outcomes split reply-to-thread per reporter; add
item 6 for separate Ctrl+Shift+X issue.
- Confidence: arch-specificity upgraded Medium → High.
- References: add both comment permalinks.
Prettier formatting applied.
Stack card content in a flex column with consistent gap and clamp the
description to 3 lines so the grid no longer goes ragged when one
plugin ships a long shortDescription.
Add author/authorUrl/stars to community-plugins.json, sort entries
alphabetically by name, and restyle the section warning to match the
existing install-warning callout.
The base body{} block was re-declaring --transition-duration-s (180ms)
and --transition-duration-l (400ms), shadowing the :root defaults
(150ms/375ms) for every element in the app regardless of theme
(body.isDarkTheme did not re-override them). That made the recent
token sweep silently drift some callers from 150ms to 180ms (+20%)
instead of the intended 1:1 swap, and left the light/dark experience
identical despite looking theme-specific.
Remove the three overrides — durations are now consistently the :root
defaults in both themes. Visible effect: task hover/select, done-toggle
SVG, and onboarding animations run about 30ms faster.
Update styling-guide.md to drop the obsolete "light theme overrides"
note and keep the bucket-rounding guidance.
Mechanical sweep replacing hardcoded values with documented design
tokens per docs/styling-guide.md. Transition durations align to the
nearest bucket (±15% tolerance); the other swaps are value-identical.
- Transitions/animations: done-toggle, heatmap, progress-circle, task,
add-task-bar, main-header.
- Colors: #fff → var(--card-bg) on the task time-badge (visible in dark
mode — badge now tracks surface color instead of staying pure white);
#333 → var(--text-color) on work-view no-tasks label.
- Spacing: 4px/16px → var(--s-half)/var(--s2) in help-box and
add-task-bar; -12px → calc(var(--s) * -1.5) in dialog-deadline.
- Radius: dialog-deadline press-enter-msg 8px → var(--radius-md).
- Font-family: drop two hardcoded 'Open Sans' declarations in
magic-side-nav/nav-item (→ inherit) so the sidenav tracks the native
--font-primary-stack.
Global typography is applied in src/styles/page.scss: body/app-root
get --font-primary-stack, --font-size-md (14px), line-height, letter-
spacing, -webkit-font-smoothing: antialiased. h1–h6 are wrapped in
:where() so their specificity is (0,0,0) — any component rule wins
trivially — and Material titles ([mat-dialog-title], .mat-mdc-dialog-
title, .mat-mdc-card-title) are excluded so Material's own type scale
is untouched. `.tabular-nums` is opt-in via the class only (the
earlier draft globally scoped `time`, which applied digit-width metrics
to non-numeric <time> content). code/kbd/pre/samp use --font-mono-stack.
Deliberately not set: text-rendering: optimizeLegibility and broad
font-feature-settings — both carry measurable paint cost on text-heavy
views. Browser defaults cover kern/liga.
Introduce coherent scales that replace ad-hoc hardcoded values and give
future components documented tokens to adopt.
Typography (src/styles/_css-variables.scss + src/styles/page.scss via
body styles in the sweep commit):
- --font-size-xs..3xl (11–28px) with --font-size-md as the body base.
- --font-weight-medium/semibold/bold (400 is the browser default, not
declared).
- --line-height-tight/snug/normal (unitless so they scale with size).
- --letter-spacing-tighter/tight/normal/wide.
- --font-mono-stack for code/kbd/pre/samp.
- --font-primary-stack reordered to lead with native OS UI fonts
(-apple-system, Segoe UI Variable Text, Roboto) with Inter/Open Sans
as graceful fallbacks.
Radius scale:
- --radius-xs/sm/md/lg/xl and --radius-pill.
- --card-border-radius retargets to var(--radius-md) (4px → 8px) and
--task-border-radius retargets to var(--radius-sm) (4px → 6px) for a
softer surface treatment; both legacy aliases stay so 30+ consumers
don't need touching.
Focus ring (keyboard accessibility):
- --focus-ring-width / -offset / -color tokens.
- .focus-ring opt-in utility class in src/styles/util.scss using
outline on :focus-visible — Material components keep their own focus
treatment.
Font stack dedup: src/styles/_fonts.scss is the single source of truth
for the stack. _css-variables.scss interpolates it into
--font-primary-stack; themes.scss passes the same list (as a literal
string, which is all mat.m2-define-typography-config accepts) — no more
"keep in sync" drift trap.
Also fix the inherited --transition-duration--leave (double dash) typo
on the light-theme body block so the override actually matches its
intended name.
docs/styling-guide.md documents all the new tokens, correct the
transition-duration values to match _css-variables.scss (-s 150ms,
-m 225ms), and adds a Focus Ring section with adoption guidance.
Group By: Tag / Group By: Project pulled tasks from every project when
used inside a specific project view, because de1b5d485c (fix for #7050)
deliberately switched the source to all undone tasks for these two
options. That closed#7050 but broke the more common case reported in
#7279 (and its duplicate #7290).
- TaskViewCustomizerService: revert customizeUndoneTasks() to the work
context's undoneTasks$, drop _allUndoneTasksWithSubTasks$ and the
cross-store selector/switchMap.
- Hide Group By: Project in a PROJECT context (it would collapse into
one group) via a new availableGroupOptions computed signal backed by
isActiveWorkContextProject$.
- Sanitize any stored group=project state inside the service's
context-load subscription, not from a lazy panel effect — the panel
is only instantiated when the mobile bottom-sheet customizer opens.
- Drop the now-redundant cross-context selection guard in
WorkViewComponent and retire its #7269 test block: after this fix
customizedUndoneTasks.list is always a subset of undoneTasks, so the
scenario those tests constructed can no longer occur.
- Stub isActiveWorkContextProject$ in all WorkContextService test
doubles so the service's toSignal() call has something to subscribe
to.
Closes#7290.
First post-release report on PR #7266 (v18.2.4): X11 widening fires
but doesn't rescue user on Ubuntu 24.04 + Intel Arrow Lake-P. Mesa
DRI still fails, GPU process segfaults in a loop. Lowers the 95%
estimate, promotes --disable-software-rasterizer to must-ship, and
upgrades core24/gpu-2404 migration urgency to 18.3.
The globalShowHide shortcut called mainWin.hide() unconditionally. On
Windows/Linux hide() removes the taskbar entry, so with no tray icon
the window became unreachable.
- macOS: use hide() (dock icon always remains).
- Windows/Linux: hide to tray only when minimize-to-tray is on AND the
tray was created; otherwise minimize() to keep a taskbar handle.
- Log whether Windows tray creation used the GUID path, to help diagnose
cases where the icon lands in the hidden overflow area.
Refs #7282
addTimeSpent and the newly-added tick action both fire every 1s from
the shared globalInterval, so taskChangeElectron$ was dispatching
updateCurrentTask twice per second during active-task focus sessions.
Collapse to a single IPC/sec with throttleTime(500, leading+trailing);
matches the sibling setTaskBarProgress$ pattern in focus-mode.effects.
Leading keeps user-triggered actions (setCurrentTask, startFocusSession)
immediate; trailing ensures the final tick in a burst still lands.
* fix(nav-item): prevent project counters from overlapping menu icon #7132
The task-count badge was positioned at right: 16px which caused it to
overlap with the additional-btn (three-dot menu, 40px wide) when the
sidebar was narrow.
Changed the default position to right: 48px (40px button + 8px padding)
to ensure the counter is always visible and doesn't overlap the menu icon.
Fixes#7132
* fix(nav-item): use inline layout on touch devices for counter (#7132)
Instead of absolute-positioning the task counter to the left of the
more-vert button, let it flow inline when .isTouchPrimary is active.
This matches the detection mechanism used by multi-btn-wrapper for
.additional-btn visibility, avoiding overlap on hybrid devices where
the @media (hover) query and the JS touch-class disagree. Desktop
behavior (absolute counter that hides on hover) is unchanged.
Co-authored-by: Rayan Salhab <rayansalhab@hotmail.com>
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
* fix(issue): prevent crash from orphan issueProviderId (#7135)
The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.
Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.
* fix(focus-mode): sync tray countdown with in-app timer during breaks
Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.
Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.
Fixes#7278
* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68
Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.
Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3
* fix: restore i18n, UI, and docs work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.
Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior
* fix(electron): restore electron + e2e work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)
Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
(re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
(setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
(electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
(SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
(this is literally the fix the bad commit's message claimed to add)
Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
checks from d64014d086 (later than c558bcab5e) while restoring the
goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
clearStaleLevelDbLocks from theirs, fs from ours).
* fix(sync): restore sync-core work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.
This PR restores only the pieces that are genuinely still missing:
- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
stale-idb-locks.ts piece was restored in the prior commit)
Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)
Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
- supersync-archive-conflict: add closing B sync to the convergence
rounds (A→B→A→B) so B is not one op behind A's final conflict-
resolution state; bump active-list assertion timeout to 15s to
cover the async reducer pipeline + sync-replay event-loop yield.
- supersync.page: fix race in changeEncryptionPassword where a stale
check icon from the previous syncAndWait() caused the method to
return before the server wipe + re-upload finished, leaving the
next client to race against partially-uploaded server state.
Now captures the check-icon state at entry and waits for it to
clear (same pattern syncAndWait uses).
* fix(persistence): increase IndexedDB retry window for session-restart locks
On Linux desktop environments (especially Flatpak with autostart),
logging out and back in can leave stale LevelDB locks for 5-15+
seconds. The previous retry window (~3.5s) was too short to outlast
this, causing "Database Error - Cannot Load Data" on every
logout/login cycle.
Increase IDB_OPEN_RETRIES from 3 to 5 and IDB_OPEN_RETRY_BASE_DELAY_MS
from 500ms to 1000ms, giving a total retry window of ~31 seconds.
Fixes#7191
* fix: update stale retry timing comments to match new constants
* fix(persistence): gate long IDB retry window on lock-related errors
_ensureInit() is awaited by every op-log read/write, and the hydrator
auto-reloads on IndexedDB open errors. Applying the ~31s retry window
to every error class could create a reload -> wait -> reload loop that
feels like a hang. Classify the error on first failure and fall back
to a short retry budget (IDB_OPEN_RETRIES_NON_LOCK=2, ~3s window) for
errors that don't look lock-related. InvalidStateError and messages
containing "backing store" still get the full window used for stale
LevelDB locks on Linux desktop session restart. See #7191.
The spec is hardened to assert concrete constant floors rather than
recomputing the backoff formula (which only proved the formula matched
itself). Also adds unit tests for the classification helper.
* fix(persistence): address review feedback on IDB retry split
- Correct stale "auto-reload" rationale in op-log-errors.const,
operation-log.const, operation-log-store.service, and archive-store.service:
the hydrator shows a blocking alert dialog on IndexedDBOpenError, it does
not auto-reload. The fail-fast argument now reflects the real reason —
every op-log read/write awaits _ensureInit(), so a 31s wait on a non-lock
error blocks the subsystem for 31s before the alert dialog surfaces.
- isLockRelatedIdbOpenError now checks `err instanceof DOMException || err
instanceof Error` before reading .name/.message, mirroring the pattern in
the sibling isConnectionClosingError. In some Electron / older runtimes
DOMException does not satisfy instanceof Error.
- Added behavioral tests for _openDbWithRetry that spy on a new
_openDbOnce testing seam: generic errors take exactly
1 + IDB_OPEN_RETRIES_NON_LOCK attempts, InvalidStateError takes up to
1 + IDB_OPEN_RETRIES, and a lock-error followed by success resolves.
- Bumped IDB_OPEN_RETRIES_NON_LOCK from 2 to 3 to match master's pre-PR
retry count (1s + 2s + 4s = ~7s ceiling). Framing this as "keep master's
existing retry count for non-lock errors, add long retry only for lock
errors" is a lower-risk scope. Adjusted the const spec's upper-bound
assertion accordingly.
- Replaced the "retries 1-5" hardcoded schedule comment in
operation-log-store.service and archive-store.service with a formula-based
comment so the two retry budgets can't drift out of sync with reality.
- Added two DOMException-shaped test cases to
operation-log.const.spec.ts covering the new predicate branch.
* docs(persistence): clean up rot-prone worked examples in retry docstrings
The new work-view.component.spec.ts uses provideMockStore with
overrideSelector for selectOverdueTasksWithSubTasks (and
selectLaterTodayTasksWithSubTasks). overrideSelector calls setResult(),
which persists across spec files and is only cleared by clearResult().
When task.selectors.spec.ts runs after work-view.component.spec.ts, the
persisted override ([] in most cases) caused four selectOverdueTasksWithSubTasks
assertions to fail with "Expected 0 to be 1".
Extend the defensive cleanup in the selectors spec's beforeEach to also
clear and release these two selectors, matching the existing pattern
documented in the same block.
Co-authored-by: Claude <noreply@anthropic.com>
Follow-up to issue #7270 and the now-closed PR #7273. Extends the
existing research doc with four new sections based on multi-agent
research, verification passes, and empirical reporter confirmation.
- §12: PR #7273 GPU startup guard framing and mechanism analysis.
- §13: Deepened research on Chromium/Electron/Mesa correctness, prior
art for crash-loop startup markers (Firefox recent_crashes, Chromium
in-process GpuMode stack, BugSnag/Sentry patterns), testing strategy
with concrete spec proposal, edge cases, and long-term strategy.
- §14: Verification pass findings (four parallel agents) — corrections
applied to §12 (v18.2.3 timeline, --disable-gpu wording), rejected
claims with evidence, outstanding attribution fixes.
- §15: Final PR re-evaluation — including why PR #7273 was closed in
favor of #7266 after the reporter empirically confirmed that
--ozone-platform=x11 resolves their launch failure on Ubuntu 22.04.
Keeps the research available for the next time this class of failure
surfaces (future Chromium bumps, Mesa ABI drift on core24/gpu-2404,
Flatpak packaging, etc.).
* fix(work-view): keep task selected when group/filter shows cross-context tasks
When Group by project/tag or cross-context filters pulled tasks from other work
contexts into the view, tapping one of those tasks dispatched setSelectedId(id)
but an effect in WorkViewComponent immediately reset it to null because the
task was not in the current context's undone/done/later/overdue/backlog lists.
On mobile this opened the bottom sheet with no task-detail-panel content,
appearing as a blank screen.
Also consult customizedUndoneTasks().list so tasks surfaced via the customizer
keep the selection alive and the detail panel can render.
Fixes#7269https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17
* test(work-view): cover selected-task retention when customizer shows cross-context tasks
Add unit tests that mirror the constructor effect in WorkViewComponent
responsible for deselecting the currently selected task. The tests pin
the fix for #7269: when the task-view customizer surfaces tasks from
other work contexts, the selection must be retained so the detail panel
can render.
Covers:
- fix: task only present in customizedUndoneTasks.list must NOT trigger
setSelectedId(null), including the subtask-traversal case
- regression: task missing from every list (including customizer) must
still deselect; customizer list being undefined/empty must not mask
deselection
- existing behaviour: null selectedTaskId is a no-op; tasks in undone /
done / laterToday / backlog / overdue (on TODAY_TAG) keep selection;
overdue tasks on non-TODAY contexts still deselect
The component has a large dependency surface and instantiates several
signals from observables in its constructor, so the test mirrors the
effect body verbatim and drives it with plain inputs rather than
standing up the full TestBed graph.
https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17
* test(work-view): upgrade retention spec to drive the real component via TestBed
Replace the logic-mirror helper with a TestBed-based harness that
instantiates the real WorkViewComponent with an overridden template and
stub providers, then drives its constructor effect through the signal
inputs. Any future change to the effect body now fails the test rather
than the mirror needing to be kept in sync by hand.
https://claude.ai/code/session_019AwFGKFEEEcBqjp7HdhU17
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Implement task parsing with sub-tasks
Added a new function to parse tasks with sub-tasks from a text input, improving task management capabilities. Updated the task addition logic to utilize this new parsing method.
Should solve #7183
* Enhance task parsing and user information
Key Changes Summary:
✅ Critical Fix#1 - 4-space indentation: Detects minimum indent and normalizes relative to it
✅ Critical Fix#2 - Mixed input: Plain-text lines no longer silently dropped; warning shown
✅ Critical Fix#3 - Deeply-nested items: Now flattened to sub-task level with user notification
✅ Minor - Sub-task dueDay: Documented that it inherits from parent (not passed independently)
✅ Suggestion - UI/UX: Updated placeholder text to show expected format with examples
* fix indentation calculation and missing isBullet: true
* fix(brain-dump-plugin): keep sub-tasks when plain-text lines interrupt
Previously, a plain-text line between a bullet and its indented sub-task
caused the sub-task to be silently dropped: the outer loop advanced past
the plain-text entry but the inner look-ahead had already exited at
indent 0, so the following indented bullet was never attached to the
parent and was rejected by the outer loop's else branch.
The sub-task scan now skips over plain-text lines while still breaking
at the next top-level bullet, so indented bullets attach to the previous
parent regardless of interleaved plain text. The plain-text line itself
is still emitted as its own top-level task by the outer loop.
Also drops dead code:
- findMinimumIndent() was declared but never called.
- dueDay on sub-task payload was forwarded but silently discarded by the
plugin bridge (sub-tasks inherit the parent's date).
And de-duplicates the "Note: ... Note: ..." prefix in the deeply-nested
warning snack.
---------
Co-authored-by: Adnoh <git@rotzefull.de>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
Widens MiscConfig.defaultStartPage from number to number | string so users
can pick any project as their startup screen in addition to Today (0),
legacy Inbox (1), Planner (2), Schedule (3), or Boards (4). Builds on
#6354.
- New StartPageSelectComponent (Formly type start-page-select) lists
built-in pages plus all non-archived, non-hidden projects. A legacy
Inbox option is rendered only while the stored value is still 1 so
existing users see their selection without any silent config write.
- Feature-gated options (Planner/Schedule/Boards) are hidden from the
dropdown when the corresponding appFeatures flag is off.
- DefaultStartPageGuard validates the selection against current state:
project must exist and not be archived or hidden; feature pages only
route when enabled; empty strings, missing projects, and disabled
features all fall back to Today instead of stranding the user.
- Deleting a project clears misc.defaultStartPage alongside the existing
tasks.defaultProjectId cleanup so a stale pointer can't linger.
- Unit tests for DefaultStartPageGuard cover every branch (including
empty-string and archived/hidden regressions) and the new effect
cleanup paths.
Editing a recurring task schedule when no archive instances exist
dispatched TaskSharedActions.updateTasks with tasks: [], tripping the
operation-log validator with "invalid entityId/entityIds (undefined)".
Guard the batch dispatch on updates.length > 0 so the empty-array path
is a no-op for all three callers that route through updateArchiveTasks.
* fix: harden archive payload repair and validation
* refactor: address PR review feedback on archive payload repair
- Sync subTaskIds with surviving subtasks in sanitizeTasksForArchiving so
the archived parent cannot carry dangling references when subtasks are
dropped during sanitization.
- Extract triple-duplicated valid-id predicate into shared
op-log/validation/is-valid-entity-id.ts and reuse from
validate-operation-payload, data-repair, and archive.service.
- Consolidate task sanitization into archive.service as the single source
of truth (it also covers writeTasksToArchiveForRemoteSync). Removes the
inline sanitizer block from task.service.moveToArchive.
- Replace console.warn / console.error in task.service.moveToArchive with
TaskLog.warn / TaskLog.err so records flow through the app log.
- Add spec covering the subTaskIds sync behavior; update task.service
spec to reflect archive.service-owned filtering.
All 8413 tests pass; lint clean.
* refactor(archive): avoid logging full task and tighten sanitizer spec
- task.service.moveToArchive now logs only the task id when it has to
wrap a single task into an array. TaskLog output is exportable, so
dumping the full task object risked leaking user-authored titles/notes.
- archive.service.spec asserts the archived parent's subTaskIds is empty
after malformed subtasks are dropped, pinning down the sanitizer's
subTaskIds sync behavior.
---------
Co-authored-by: Aamer Akhter <aamer_akhter@users.noreply.bitbucket.org>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
* Update Croatian translation
This update corrects the current Croatian translation (see #4964).
The update is based on the English version of the 2nd April 2026.
Please update as soon as possible, because of the above metioned issue!
Thank you.
* Update Croatian
Corrected translation errors found by @johannesjo. See issue #7148.
I hope got them all!
* Update Croatian translations in hr.json
Corrected SyntaxError (missing comma) and updated translation string
* fix(i18n): apply review fixes to Croatian translation
- Restore <strong> and unsynced-changes warning in SUPER_SYNC FORCE_OVERWRITE_CONFIRM
- Add 26 keys added to en.json since PR base (BOARDS sort/tag-match, SYNC errors, FOCUS_MODE sounds, PLUGINS size limits, COPY_URL)
- Remove renamed GCF.FOCUS_MODE.L_IS_PLAY_TICK (superseded by L_FOCUS_MODE_SOUND)
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>