PR 1 of 3 for issue #559 — docs and conventions, no behaviour change.
- New `docs/learnings/patching-minified-js.md` covering anchor
selection, identifier capture (`\w` vs `$`), beautified
false-negative trap, whitespace tolerance, replacement-string
escaping, idempotency, multi-site coordination, lastIndexOf
disambiguation, and the SHA-256-pinned hypothesis-verification
recipe.
- New `CONTRIBUTING.md` as a navigation hub: scope policy
(no net-new features outside Linux-environment parity), upstream
routing, subsystem owners, PR checklist, AI-assisted contribution
disclosure format, and the patch-script regex intent comment +
markdown wrapping conventions.
- Fix CLAUDE.md:126 example regex `\w+` → `[$\w]+` (same class of
bug the new learnings doc documents).
- CLAUDE.md learnings index entry for the new doc.
PRs 2 (`verify-cowork-patches.sh` + BATS) and 3 (silent-no-op
WARNING retrofits) follow.
Refs #559
Co-authored-by: Claude <claude@anthropic.com>
* feat(linux): hybrid titlebar mode for clickable in-app topbar
Default `CLAUDE_TITLEBAR_STYLE` is now `hybrid`: native OS frame
plus a BrowserView preload shim that convinces claude.ai's bundle
to render its in-app topbar (hamburger / sidebar / search / nav /
Cowork ghost). Stacked layout instead of Windows's combined bar,
but every button is clickable.
Why not the upstream `frame:false` + WCO config: investigation
(see docs/learnings/linux-topbar-shim.md) ruled out
`titleBarOverlay`, `titleBarStyle:'hidden'`, and the `.draggable`
CSS class as the source of the topbar click-eating drag region.
The remaining cause is a Chromium-level implicit drag region for
`frame:false` windows that exists on both X11 and Wayland and has
no Electron-API knob. With `frame:true` the OS handles dragging
and Chromium pushes no drag-region map, so the buttons receive
mouse events normally.
Modes:
- `hybrid` (default) — system frame + shim, topbar visible and
clickable
- `native` — system frame, no shim, no in-app topbar
- `hidden` — frameless + WCO config, matches Windows/macOS
upstream; topbar visible but not clickable on Linux. Kept for
Wayland comparison and future investigation
Tests: tests/launcher-common.bats grew 16 cases covering
`_resolve_titlebar_style`, `build_electron_args` flag selection
per mode, and `setup_electron_env` env-var wiring per mode.
`claude-desktop --doctor` now reports the resolved mode and
warns when `hidden` is set.
Co-Authored-By: Claude <claude@anthropic.com>
* docs(learnings): add hybrid-mode screenshot
Visual reference of the stacked layout: DE-drawn titlebar on top
with native window controls, claude.ai's in-app topbar
(hamburger / search / back-forward) immediately below it.
Co-Authored-By: Claude <claude@anthropic.com>
* docs(learnings): fix codespell hit (Pre-emptive → Preemptive)
Codespell flags hyphenated "Pre-emptive" as a misspelling of
"Preemptive". Drops the hyphen to clear the spellcheck CI gate
on PR #538.
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
* docs(learnings): document MCP double-spawn upstream bug (#526)
Captures the reporter's root-cause analysis for issue #526: stdio MCP
servers in claude_desktop_config.json get spawned twice when both the
chat panel and the Code/Agent (Cowork) panel are active. The
duplication happens entirely in upstream Anthropic Claude Desktop main
(LocalSessions and LocalAgentModeSessions each hold an independent
Claude Agent SDK query whose stdio transport bypasses the global hZ
MCP registry).
Includes verification that this packaging is not implicated, the
lockfile + idempotent-write workaround pattern for affected MCP
authors, and routing guidance for upstream reports.
Co-Authored-By: Claude <claude@anthropic.com>
* docs(learnings): simplifier pass on MCP double-spawn entry
Drop redundant "Anthropic" qualifier in Status section and reword
CLAUDE.md index bullet to noun-phrase form matching siblings.
Co-Authored-By: Claude <claude@anthropic.com>
* docs(learnings): apply review fixes from #527
- Fix `LocalAgentModeSessions` IPC namespace: add missing `_$_`
separator (was `claude.web_$_LocalAgentModeSessions_*`, should be
`claude.web_$_LocalAgentModeSessions_$_*`). Verified against the
channel names in the actual minified source.
- Add back the `Logs prefix` column (`[CCD]` / `[LAM]`) the original
issue body had — these are the literal grep targets in
`~/.config/Claude/logs/` for confirming the bug hit.
- Re-route the secondary upstream venue from `anthropics/claude-code`
to `anthropics/claude-agent-sdk-typescript`. The SDK transport
(`spawnLocalProcess` / `Du.spawn`) lives in the SDK's own public
repo (issues enabled); pointing at `claude-code` while saying the
CLI isn't on the spawn path is the exact contradiction the warning
paragraph below it tries to prevent.
- Workaround note: reclaim a stale lock via `rename()` over the path,
not `unlink()` then re-open. Heads off the obvious-but-racy port
for anyone copying the pattern.
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
* fix: update Linux tray icon in place on OS theme change
Avoids a StatusNotifierItem re-registration race on KDE Plasma
where the old SNI remains registered when the new one appears,
resulting in two tray icons side by side until session logout.
`patch_tray_menu_handler` already bounds the race with a 250 ms
delay after `tray.destroy()`, but that's not enough on all setups
(reproduced on Fedora 43 KDE Plasma 6.6.4 + Wayland). Widening the
delay just moves the goalposts; the race is structural.
Fix: inject a fast-path before the existing destroy+recreate block
in the tray rebuild function. When the tray already exists and
isn't being disabled, update its icon and context menu in place
via `setImage` + `setContextMenu` — the existing StatusNotifierItem
stays registered, no DBus re-registration, no race. The slow path
(destroy + delay + re-create) is kept for the initial creation and
the tray-disable cases where it's unavoidable.
All five minified locals needed by the fast-path (tray function,
tray variable, electron module, menu function, icon path const,
menuBarEnabled flag) are extracted dynamically; the idempotency
guard re-keys off the post-rename `setImage(...)` sequence.
Triggered in KDE System Settings by any of Appearance → Colors /
Plasma Style / Global Theme, which all fire the same
`nativeTheme.on('updated')` signal.
Follow-up to #491. The broader submenu work from that PR stays
parked on features/change-icon-color pending the scope discussion
in #492; this PR ships only the duplicate-tray-icon fix that
@aaddrick asked to split out.
Co-Authored-By: Claude <claude@anthropic.com>
* fix(tray): tighten in-place patch extraction guards
Drop the redundant `electron_var_re_local` local — `electron_var_re`
is already a sourced global from `_common.sh` with the same value.
Replace the silent `head -1` on `enabled_var` extraction with an
explicit count-and-bail. The grep matches `const X=fn("menuBarEnabled")`
across the whole file; today there's exactly one site (inside the
tray function), but if upstream ever ships a second the previous
code would silently bind to whichever the minifier emitted first.
Bail loudly with a count diagnostic instead.
Verified on the live 1.3883.0 build asar: all five extractions
resolve (`Nh`/`wAt`/`t`/`e`) — note the symbol drift vs. the
build-reference's `fh`/`CZe`. Fast-path injects, JS validates,
idempotent re-run confirmed, duplicate-icon repro gone on Nobara
KDE Plasma 6 (Wayland) under Appearance → Colors / Plasma Style /
Global Theme.
Co-Authored-By: Claude <claude@anthropic.com>
* docs(readme): credit @IliyaBrook for tray duplicate-icon fix
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: aaddrick <aaddrick@gmail.com>
Phase 5 docs follow-up to #493. The plan doc was deleted in #511;
this replaces it with a learnings file aimed at future maintainers
(and future-me) rather than a design spec.
docs/learnings/apt-worker-architecture.md covers:
- The problem (100MB push cap) and why other fixes were rejected
- Redirect chains for both legacy github.io users and direct
pkg.<domain> users
- Why raw.githubusercontent.com is the origin (Pages 301 loop)
- Why Pages emits http:// (no cert, and why the cert can't be had)
- File map (worker source, wrangler.toml, deploy workflow, heartbeat)
- Credential ownership (Cloudflare account, registrar, API token scopes)
- Heartbeat failure runbook — 5 ordered steps to work through
- Rollback paths and documented fallbacks if Cloudflare becomes
unavailable
- Known gotchas including the smoke-test-URL-is-intentionally-github.io
note so future cleanup passes don't "fix" it
CLAUDE.md gains:
- Link to the new learnings file in the Learnings list
- New "Distribution" section under CI/CD with a one-paragraph summary
and pointers to the key files
Refs #493
Updates agent definitions, learnings, CLAUDE.md, and BUILDING.md so
path references point at the new module files instead of the old
monolithic build.sh.
Agent definitions:
.claude/agents/issue-triage.md — table of per-category
investigation paths now points at scripts/patches/*.sh and
scripts/packaging/*.sh instead of "build.sh (search patch_X)".
.claude/agents/electron-linux-specialist.md — patching-functions
table now includes each function's file location; directory tree
illustration reflects the new scripts/ layout.
Documentation:
CLAUDE.md — "Working with Minified
JavaScript" section points at scripts/patches/*.sh; frame-fix
injection attributed to scripts/patches/app-asar.sh; the
version-bump checks now grep scripts/setup/detect-host.sh.
docs/BUILDING.md — automated version
detection paragraph now mentions scripts/setup/detect-host.sh as
the file that holds the URLs.
docs/learnings/cowork-vm-daemon.md — Patch 6 pointer now
says scripts/patches/cowork.sh; line-number references dropped in
favour of anchor-based search (line numbers drift between releases).
docs/learnings/plugin-install.md — Key Files section
points at scripts/patches/cowork.sh for patch_cowork_linux.
Historical changelog-style references (e.g. docs/cowork-linux-handover.md
describing what was "added to build.sh" during initial cowork work)
are intentionally left unchanged — they describe a point-in-time state
of the codebase.
Co-Authored-By: Claude <claude@anthropic.com>
* docs: document Anthropic & Partners plugin install flow (#396)
Captures the non-obvious bits of the plugin install flow that came
out of the #396 / PR #435 investigation:
- Remote renderer architecture (claude.ai in BrowserView) and why
the main process can't control pluginContext.mode or
pluginSource.
- Current 1.3109.0 install gate, listing filter, and A0() gating
points with line citations.
- Backend endpoints, identity headers injected by the app, and
auth surface.
- Full post-mortem of issue #396: old 1.1.7714 gate vs current
1.3109.0, why it reproduced in the Directory, and the
coordinated upstream fix.
- Live investigation recipe: enabling main-process DevTools,
header-spoofing harness, breakpointing the install gate.
- Tip about using reference-source.tar.gz from releases for
cross-version source diffing.
Co-Authored-By: Claude <claude@anthropic.com>
* docs: tighten redundant intro in plugin-install learning
Collapse the two near-duplicate sentences in "Why This Exists"
into one. The bold insight already states the renderer is
remote; the follow-up then repeated it.
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
Capture the architecture and failure modes of the Linux cowork-vm
daemon — respawn logic, crash diagnosis, and the one-shot-guard /
preserved-image pitfalls that caused issue #408. Intended for
future contributors (human or AI) who need to navigate this area
without re-deriving it from minified JS.
Co-Authored-By: Claude <claude@anthropic.com>
Create docs/learnings/ for hard-won technical knowledge that isn't
obvious from code or docs alone. Reference from CLAUDE.md so
contributors (human and AI) consult it before working on related areas.
First entry covers NixOS Electron resource path resolution,
/proc/self/exe symlink behavior, testing without NixOS, and why
the co-located binary approach was chosen over alternatives.
Co-Authored-By: Claude <claude@anthropic.com>
* fix: extract minified vars dynamically in cowork patch 9 (#344)
Patch 9 (smol-bin VHDX copy) hardcoded minified variable names
(Qe, ft, vg, tt, uX) which change between upstream releases,
causing "Qe is not defined" crashes at runtime.
Extract all 6 variables dynamically from the nearby win32 block
using regex patterns that handle both minified and beautified code.
Add diagnostic logging of extracted variable names.
Also document the repo versioning system (REPO_VERSION,
CLAUDE_DESKTOP_VERSION variables and tag format) in CLAUDE.md.
Fixes#344
Co-Authored-By: Claude <claude@anthropic.com>
* style: simplify console.log calls in cowork patch 9
Remove redundant comment restating the regex pattern, and replace
unnecessarily split string concatenations in console.log calls
with template literals (consistent with the existing pattern on
the final patchCount summary line).
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
- Change PreToolUse lint hook from gh pr create to git push
- Add /lint skill for manual linting (available to Claude and user)
- Add linting guidance to CLAUDE.md (fix issues, disable directives as last resort)
Co-Authored-By: Claude <claude@anthropic.com>
Add section to CLAUDE.md with lessons learned from RPM signing:
- Research target system constraints before implementing
- Consider concurrency when multiple jobs push to same branch
- Test full pipeline before merging
- Common CI pitfalls table (GPG flags, push races, version formats)
Co-Authored-By: Claude <claude@anthropic.com>
Add guidance on reviewing code state at time of issue creation
to identify if issues have already been addressed in later commits.
Co-Authored-By: Claude <claude@anthropic.com>
Code simplifications from cdd-code-simplifier:
- Consolidate duplicate argument validation in build.sh
- Use early returns to reduce nesting depth
- Convert multi-line conditionals to single-line where clearer
- Simplify icon array in build-deb-package.sh
- Refactor appimagetool discovery loop in build-appimage.sh
Additional changes:
- Update Claude Desktop URLs to v1.1.673 (from main)
- Fix check-claude-version.yml patterns for lowercase variable names
- Add version check guidance to CLAUDE.md
Co-Authored-By: Claude <claude@anthropic.com>
The nativeTheme.on("updated") event fires during app startup almost
simultaneously with the initial tray creation from app.on("ready").
Both calls check the mutex guard at nearly the same time, both see
false, and both proceed to create tray icons - causing DBus
"already exported" errors and duplicate tray icons.
This fix adds a timestamp variable and skips tray function calls in
the theme handler during the first 3 seconds of app runtime, allowing
the initial tray creation to complete before theme-triggered updates
are processed.
Also adds CLAUDE.md with development notes including guidelines for
working with minified JavaScript (use regex patterns, handle spacing
variations, extract variable names dynamically).