Safe Update/Merge
Safely merge upstream OpenClaw updates without destroying plugin/skill injections, custom UI tabs, or workspace features. Two-phase: Phase 1 (automated) merg...
Description
name: safe-merge-update description: "Safely merge upstream OpenClaw updates without destroying plugin/skill injections, custom UI tabs, or workspace features. Two-phase: Phase 1 (automated) merges, builds, and restarts the gateway from a safe-merge branch — without touching local-desktop-main. Phase 2 (user-confirmed) promotes to local-desktop-main and pushes to the fork remote only after the user verifies the gateway is healthy." metadata: openclaw: emoji: "🔄" requires: bins: ["git"] anyPkgMgr: ["npm", "pnpm"] optionalBins: - name: "claude" description: "Claude Code CLI — only required for --auto-resolve conflict resolution. Install: npm i -g @anthropic-ai/claude-code" - name: "python3" description: "Used by preflight.sh to write the JSON report. Available by default on most Linux/macOS systems." - name: "systemctl" description: "Used to restart the gateway after a successful merge (systemctl --user restart openclaw-gateway). Required only on systemd-based Linux hosts." env: - REPO_DIR optionalEnv: - name: ANTHROPIC_API_KEY description: "Required by the claude CLI for conflict resolution. May already be set in your shell environment or claude config (~/.claude/)." - name: UPSTREAM_REMOTE description: "Git remote name for openclaw/openclaw (default: upstream)" - name: TARGET_REMOTE description: "Git remote to push result to (default: myfork)" - name: TARGET_BRANCH description: "Branch to promote on success (default: local-desktop-main)" - name: PACKAGE_MGR description: "npm or pnpm — auto-detected from lockfile if unset" gitRemotes: - name: "upstream" description: "OpenClaw upstream repo (github.com/openclaw/openclaw)" envOverride: "UPSTREAM_REMOTE" - name: "myfork" description: "Your fork remote to push to" envOverride: "TARGET_REMOTE" gitBranches: - name: "local-desktop-main" description: "Branch to promote merged result to" envOverride: "TARGET_BRANCH"
Safe Merge Update
Merges upstream OpenClaw changes into your fork while preserving all custom code: plugin registrations, custom UI tabs, workspace skills, controllers, and state extensions.
⚠️ Operator Warnings — Read Before Running
This skill performs high-impact, partially irreversible operations. Understand what it does before executing in a production environment:
| Operation | Phase | Impact |
|---|---|---|
git merge upstream/main |
Phase 1 | Creates safe-merge-YYYY-MM-DD branch and merges — no changes to local-desktop-main |
npm run build / pnpm run build |
Phase 1 | Downloads packages if lockfile changed (network); builds in place |
systemctl --user restart openclaw-gateway |
Phase 1 | Restarts the live gateway — brief downtime; gateway now runs from safe-merge build |
Conflict resolution via claude CLI |
Phase 1 (optional) | Passes redacted file content to the Claude API; Edit+Read tools only — Bash explicitly excluded |
git push --force to TARGET_REMOTE/TARGET_BRANCH |
Phase 2 only (--promote) |
Overwrites remote branch — only runs after user confirms gateway is healthy |
git branch -D safe-merge-* |
Phase 2 only (--promote) |
Deletes temp branch after successful promotion |
Prerequisites
Before running, verify:
git remote -vshowsupstreampointing togithub.com/openclaw/openclaw(or setUPSTREAM_REMOTE)git remote -vshowsmyforkpointing to your fork (or setTARGET_REMOTE)- Branch
local-desktop-mainexists locally (or setTARGET_BRANCH) claudeCLI is installed and authenticated (claude --version)npmorpnpmis available for builds- You are OK with the gateway restarting automatically on success
Conflict Resolution & Secret Redaction
When merge conflicts occur, the script:
- Runs
scripts/redact-secrets.shon each conflicted file — replaces secrets with[REDACTED_N]placeholders, writes the redaction map to a per-file temp file in a mode-700 directory - Invokes the
claudeCLI with--allowedTools Edit,Read(noBash) to resolve conflict markers in the redacted files — the model cannot execute shell commands - After claude exits, the script runs
git add -A && git commit --no-edititself - Restores secrets from the redaction map, then deletes the map files immediately
If you prefer manual resolution, skip claude entirely: fix conflicts yourself and run --resume.
Flags
| Flag | Description |
|---|---|
--dry-run |
Fetch remotes, show divergence and estimated conflict count, then exit — no changes made |
--no-auto-resolve |
Stop on conflicts instead of invoking claude — leaves the safe-merge branch for manual resolution, then use --resume |
--resume <branch> |
Skip the merge; run build → restart on an existing safe-merge branch (use after manual conflict resolution) |
--promote <branch> |
Phase 2 — run after user confirms the gateway is healthy. Force-pushes the safe-merge branch to TARGET_REMOTE/TARGET_BRANCH, switches local branch, deletes temp branch. This is the only step that modifies local-desktop-main. |
All Environment Variables
| Variable | Default | Description |
|---|---|---|
REPO_DIR |
(required) | Path to your OpenClaw repository |
UPSTREAM_REMOTE |
upstream |
Remote name for openclaw/openclaw |
UPSTREAM_BRANCH |
main |
Branch to pull from upstream |
TARGET_REMOTE |
myfork |
Remote to force-push result to |
TARGET_BRANCH |
local-desktop-main |
Branch to promote on success |
LOCAL_BRANCH |
current branch | The branch being merged from (used by preflight.sh for divergence stats) |
PACKAGE_MGR |
auto-detect | npm or pnpm (auto-detected from lockfile) |
How It Works
- Pre-flight: Fetch upstream, compute divergence, detect conflicts
- AI Merge: The agent resolves each conflict using the merge manifest as intent context
- Validate: Build check, tab verification, protected pattern scan
- Report: Announce results back to the requesting session
Security & Privacy
Model Usage
This skill uses the claude CLI (the Anthropic Claude Code CLI, not the OpenClaw agent model) for conflict resolution. The CLI must be installed separately and authenticated with an Anthropic API key in your environment. It is invoked with --allowedTools Edit,Read — the Bash tool is explicitly excluded, so the model can only read and edit files, not execute shell commands.
The model sees only the redacted content of conflicted files (see Secret Redaction below) — not the entire repository.
To control which Claude model is used, configure it in your ~/.claude/config.json or set the model via claude CLI flags.
Secret Redaction
Before any file content is sent to the claude CLI for conflict resolution, it passes through scripts/redact-secrets.sh which detects and replaces:
- API keys (OpenAI
sk-, GitHubghp_, Slackxoxb-, AWSAKIA, etc.) - Bearer/Basic auth tokens
- Private keys (RSA, EC, OPENSSH)
- Connection strings with embedded passwords
- Config values for
password,secret,token,apiKeyfields
Detected secrets are replaced with [REDACTED_N] placeholders.
How the map is stored: redact-secrets.sh requires fd 3 to be open and writes the redaction map to whatever file fd 3 points to. In safe-merge-update.sh, fd 3 is wired to a per-file temp file inside a mode-700 temp directory (mktemp -d). So the map is written to disk — in a private, process-owned temp directory — and deleted immediately after secret restoration. It is never written to stdout, stderr, or any shared/world-readable path.
If your security policy requires no disk writes, mount the temp dir on a tmpfs:
REDACT_MAP_DIR=$(mktemp -d)
mount -t tmpfs -o size=1m,mode=700 tmpfs "$REDACT_MAP_DIR"
# ... run safe-merge-update.sh with REDACT_MAP_DIR set ...
umount "$REDACT_MAP_DIR" && rmdir "$REDACT_MAP_DIR"
What is sent to the claude CLI: Only the redacted content of conflicted files. Claude resolves conflict markers (<<<<<<<, =======, >>>>>>>) using only the Edit and Read tools — Bash is not granted, so the model cannot execute shell commands. The script itself runs git add -A && git commit --no-edit after claude finishes.
Backups in /tmp/safe-merge/backups/ contain only redacted content — they are created after redaction and never contain plaintext secrets.
What Gets Sent to the Model
- Only conflicting file diffs are sent (not the entire repository)
- All secrets are redacted before transmission (see above)
- The merge manifest describes file intents and protected patterns
.envfiles are never included in merge prompts
Build Execution
The validation phase runs pnpm install --ignore-scripts, pnpm build, and pnpm ui:build to verify the merge compiles.
pnpm install will download packages from the npm registry. This is network activity. --ignore-scripts is always passed to suppress preinstall/postinstall lifecycle hooks from running untrusted code.
Before proceeding past pre-flight:
- Review upstream
package.jsondiffs in the pre-flight report - Check for new dependencies you don't recognize before running install
- For maximum safety, run the full merge in an isolated environment (container, VM, or disposable clone)
SKILL.md says "No Network Installs" — this refers to the skill package itself (no curl/wget/npm install in the skill's own setup). It does NOT mean the merge workflow avoids network; git fetch upstream and pnpm install both require network access.
Backups (Non-Negotiable)
Before ANY file edits, the skill creates a full backup of every conflicting file at /tmp/safe-merge/backups/ preserving directory structure. Backups are created after secret redaction — they never contain plaintext secrets. If the merge goes wrong, restore from backups and re-run secret restoration.
No Network Installs
This skill contains no install scripts that download from external URLs. All files are local to the skill package. The only network activity is git fetch upstream and your normal model API calls.
Invocation
Via UI
Click the ↑ Update button in the topbar (right of Health pill).
Via Chat
/update — or ask: "run a safe merge update"
Architecture
Fully Automated Pipeline (scripts/safe-merge-update.sh)
The primary entry point. Run this directly — it handles the entire workflow end-to-end:
cd /path/to/openclaw
# Safe first step — shows divergence, makes no changes
REPO_DIR=. ./scripts/safe-merge-update.sh --dry-run
# Phase 1 — merge, build, restart gateway from safe-merge branch
# local-desktop-main is NOT touched; no remote push yet
REPO_DIR=. ./scripts/safe-merge-update.sh
# Manual mode — stop on conflicts for review instead of invoking claude
REPO_DIR=. ./scripts/safe-merge-update.sh --no-auto-resolve
# Resume after fixing conflicts manually (still Phase 1)
REPO_DIR=. ./scripts/safe-merge-update.sh --resume safe-merge-2026-03-02
# Phase 2 — after verifying the gateway is healthy, promote to local-desktop-main
REPO_DIR=. ./scripts/safe-merge-update.sh --promote safe-merge-2026-03-02
Two-phase design — local-desktop-main is never touched until the user confirms:
Phase 1 (automated):
- Checks you're on
local-desktop-mainwith a clean working tree --dry-run: fetches, shows upstream divergence + estimated conflict count, exits — no changesgit fetch --all- Prunes any stale
safe-merge-*branches (prevents accumulation) - Creates
safe-merge-YYYY-MM-DDbranch - Runs
git merge upstream/main --no-edit - If conflicts → redacts secrets (maps written to mode-700 temp dir via fd 3, deleted after restore), invokes
claude --allowedTools Edit,Read(Bash excluded), restores secrets, runsgit commit— use--no-auto-resolveto stop for manual review pnpm build+pnpm ui:build- Restarts gateway from current working tree (safe-merge branch build)
- Exits with confirmation prompt —
local-desktop-mainis unchanged, no remote push yet - On build failure: leaves safe-merge branch intact for investigation
Phase 2 (user-confirmed via --promote):
- User verifies gateway is healthy
git push myfork safe-merge-YYYY-MM-DD:local-desktop-main --forcegit checkout local-desktop-main && git reset --hard safe-merge-YYYY-MM-DDgit branch -D safe-merge-YYYY-MM-DD
Rollback before confirming: gateway restart reverts to previous build; local-desktop-main was never touched.
Claude conflict resolution strategy (baked into the script):
- local-desktop-main UI/vault/navigation customizations → prefer HEAD (ours)
- Upstream security and bug fixes → prefer incoming (theirs)
- Additive changes on both sides → keep both
- TypeScript type unions → union both sides
- Genuinely ambiguous → prefer HEAD
Resume mode — for cases where Claude's auto-resolution needs a manual touch:
# Fix conflicts manually, then:
./scripts/safe-merge-update.sh --resume safe-merge-2026-03-02
# Skips merge, runs build → push → cleanup
Supporting Scripts (for advanced / manual use)
scripts/preflight.sh— read-only analysis, produces JSON report at/tmp/safe-merge/preflight-report.jsonscripts/validate.sh— post-merge build +mustPreservepattern checksscripts/merge-agent-prompt.md— prompt template for per-file conflict resolution (used when invoking Claude manually)scripts/redact-secrets.sh— secret detection and redaction before model transmission
Branch Strategy
- Working branch:
local-desktop-main(your fork's primary branch) - Temp merge branch:
safe-merge-YYYY-MM-DD(created and destroyed per run) - Upstream source:
upstream/main(openclaw/openclawofficial repo) - Push target:
myfork/local-desktop-main - Stale safe-merge branches are auto-pruned at the start of each run — no accumulation
Phases
Phase 1: Pre-flight (scripts/preflight.sh)
Run automatically when invoked. Produces a report at /tmp/safe-merge/preflight-report.json.
What it does:
- Fetches upstream (
git fetch upstream) - Computes commit divergence (ahead/behind counts)
- Lists conflicting files via
git merge-tree - Checks each conflict against the merge manifest for protection status
- Creates a temporary worktree for dry-run merge (avoids touching your working tree)
Pre-flight report includes:
- Divergence stats
- List of conflicting files with protection status
- Recommended strategy per file (keep-ours, ai-merge, accept-upstream)
Environment variables:
REPO_DIR— Path to your OpenClaw repo (must be set explicitly)UPSTREAM_REMOTE— Upstream remote name (default:upstream)UPSTREAM_BRANCH— Upstream branch (default:main)
Phase 2: Merge & Conflict Resolution
The agent performs the actual merge and resolves conflicts:
- Create merge branch —
git checkout -b safe-merge-YYYY-MM-DD - Examine conflicts before merging — read both our version and upstream's version of each conflicting file to understand what each side changed
- Run the merge —
git merge upstream/main --no-commit --no-ff - For each conflicting file:
a. Read the conflict markers to understand the exact diff
b. Check
MERGE_MANIFEST.jsonfor intent and strategy (keep-ours,accept-upstream,ai-merge) c. Resolve: write the clean merged file preserving our customizations + upstream improvements d.git addthe resolved file - Verify auto-merged protected files — even files that auto-merge need checking: grep for
mustPreservepatterns to confirm our custom code survived - Secret redaction (for complex conflicts requiring prompt-based resolution):
scripts/redact-secrets.shreplaces secrets with[REDACTED_N]placeholders- Redaction map is written to a per-file temp file in a mode-700 directory via fd 3; it is never written to stdout, stderr, or any shared path, and is deleted immediately after secret restoration
- After resolution, restore secrets from map, then delete the temp directory
Key principle: Always examine both sides of a conflict BEFORE attempting resolution. Understanding what upstream changed and what we customized is essential for correct merges.
Phase 3: Validation (scripts/validate.sh)
After all conflicts are resolved:
pnpm install --ignore-scripts— install any new dependencies (lifecycle scripts suppressed)pnpm build— compile the gatewaypnpm ui:build— compile the Control UI- Protected pattern scan — verify
mustPreservepatterns from the manifest still exist - Tab verification — check that custom UI tabs are still registered
Phase 4: Commit & Report
- Commits on the merge branch (
safe-merge-YYYY-MM-DD) — never directly onmain - Commit message includes: upstream version, commit count, conflict resolution summary, file counts
- Reports to user: files resolved, strategies used, build status, any warnings
- User decides whether to merge the branch into
mainand push:git checkout main git merge safe-merge-YYYY-MM-DD git push origin main
Configuration
| Variable | Default | Description |
|---|---|---|
REPO_DIR |
(required, declared in metadata) | Path to your OpenClaw repository |
SAFE_MERGE_MODEL |
(agent's current model) | Model override for conflict resolution |
UPSTREAM_REMOTE |
upstream |
Git remote name for upstream |
UPSTREAM_BRANCH |
main |
Upstream branch to merge from |
Merge Model Selection
The model used for AI conflict resolution can be configured in two ways:
-
Via the UI modal — The update modal includes a "Merge Model" dropdown that lists all available models (same catalog as Agents → Model Selection). The selection is persisted in
localStorageunder the keyopenclaw-merge-modeland survives page reloads. When a model is selected and "Run Safe Merge" is clicked, the merge prompt includesSAFE_MERGE_MODEL=<selected-model>. -
Via environment variable — Set
SAFE_MERGE_MODELbefore invoking the skill (e.g., in the agent's env config or inline).
If neither is set, the skill uses the agent's currently configured primary model.
The dropdown appears on both the initial "Check for Updates" screen and the results screen, so you can change it at any point before starting the merge.
Files
| File | Purpose |
|---|---|
SKILL.md |
This file — skill instructions |
MERGE_MANIFEST.json |
Protected files, intents, mustPreserve patterns |
scripts/preflight.sh |
Pre-flight analysis (read-only, no modifications) |
scripts/validate.sh |
Post-merge build and pattern validation |
scripts/merge-agent-prompt.md |
Prompt template for per-file conflict resolution |
scripts/redact-secrets.sh |
Secret detection and redaction before model transmission |
update-modal.ts |
Reference copy of the UI update modal component (source of truth: ui/src/ui/views/update-modal.ts) |
references/bg-sessions-backend.ts |
Reference: src/gateway/server-methods/bg-sessions.ts |
references/bg-sessions-controller.ts |
Reference: ui/src/ui/controllers/bg-sessions.ts |
references/bg-sessions-views.ts |
Reference: ui/src/ui/views/bg-sessions.ts |
Background Sessions Panel
A right-side drawer panel that lets you watch and talk to background/cron subagents in real-time, without leaving the Control UI.
How to Open
- Click the updates badge (e.g. "148 Updates") in the status bar — opens the update modal and slides in the background sessions panel simultaneously
- Can also be opened programmatically:
openBgSessionsPanel(client, state)
What It Shows
- Session list dropdown — all isolated/cron sessions, with 🟢 running / ⚪ idle indicator and a human-readable label extracted from the cron job name
- Live transcript — scrollable, role-colored messages:
- 🟣
You(user injections) - 🟢
Agent(assistant responses) - 🟡
→ tool(tool calls with args) - ⚪
← result(tool results, truncated) - 🔴
system(compaction events)
- 🟣
- Auto-refresh — polls
bgSessions.historyevery 3 seconds while panel is open - Send messages — text area at the bottom; Enter sends, Shift+Enter newlines. Routes through
chat.sendRPC using the selectedsessionKey
Architecture
Backend (src/gateway/server-methods/bg-sessions.ts):
bgSessions.list— readssessions.jsonfor the default agent, filters for isolated/cron session keys (UUID format:agent:main:<uuid>), returns label, updatedAt, running status (lock file presence)bgSessions.history— loads the.jsonltranscript file viareadSessionMessages(), simplifies to{ role, text, timestamp, toolName }arraybgSessions.sendis intentionally omitted — the UI callschat.senddirectly with the targetsessionKey
Controller (ui/src/ui/controllers/bg-sessions.ts):
openBgSessionsPanel(client, state)— sets panel open, fetches sessions, starts 3s pollcloseBgSessionsPanel(state)— clears panel open flag, stops pollloadBgSessions(client, state)— fetches session list via RPCloadBgSessionHistory(client, state, key)— fetches transcript via RPCselectBgSession(client, state, key)— changes selected session, reloads historysendBgMessage(client, state)— callschat.sendRPC with session key + message, refreshes historystartBgSessionsPolling(client, state)/stopBgSessionsPolling()— manage setInterval
View (ui/src/ui/views/bg-sessions.ts):
renderBgSessionsPanel(state, client)— full Lit HTML panel, rendered as overlay inapp-render.tsbgSessionsPanelStyles— exported CSS (for reference; styles are embedded in the view's html template)- Panel is an overlay div (fixed inset-0, z-index 9000) with a right-anchored 520px panel; click outside to close
State fields (added to AppViewState and OpenClawApp):
bgSessionsPanelOpen: boolean;
bgSessionsList: BgSession[] | null;
bgSessionsLoading: boolean;
bgSessionsSelectedKey: string | null;
bgSessionsHistory: BgMessage[] | null;
bgSessionsHistoryLoading: boolean;
bgSessionsInput: string;
bgSessionsSending: boolean;
Wiring in app-render.ts
The panel is rendered as the last overlay before the closing </div>:
${state.bgSessionsPanelOpen && state.client ? renderBgSessionsPanel(state, state.client) : nothing}
The update badge click handler also calls openBgSessionsPanel:
@click=${() => {
// ...existing update modal logic...
if ((state as any).client) { openBgSessionsPanel((state as any).client, state as any); }
}}
MERGE_MANIFEST.json — Add These Entries
When merging future upstream changes, protect these new files:
"src/gateway/server-methods/bg-sessions.ts": {
"intent": "New RPC handlers for background session listing and history (bgSessions.list, bgSessions.history)",
"strategy": "keep-ours"
},
"ui/src/ui/controllers/bg-sessions.ts": {
"intent": "Controller for the background sessions panel — load, poll, send, select",
"strategy": "keep-ours"
},
"ui/src/ui/views/bg-sessions.ts": {
"intent": "Right-side drawer panel for viewing/talking to cron subagents",
"strategy": "keep-ours"
}
Also add these mustPreserve patterns to the relevant existing protected files:
ui/src/ui/app-render.ts:renderBgSessionsPanel,openBgSessionsPanel,bgSessionsPanelOpenui/src/ui/app.ts:bgSessionsPanelOpen,bgSessionsList,bgSessionsSelectedKeyui/src/ui/app-view-state.ts:bgSessionsPanelOpen,bgSessionsList
UI Update Modal
Clicking the topbar update button (in any state) opens an update modal with a guided flow:
- Check for Updates — asks the user to confirm, then calls
update.checkUpstreamRPC withforce: true(bypasses cache) - Status Result — shows upstream divergence: commits behind, commits ahead, or "Up to date"
- Action Buttons — "⚡ Run Safe Merge" (if behind) or "🔄 Run Merge Anyway" (if up to date) — both send the merge prompt to the chat session
The modal is rendered by ui/src/ui/views/update-modal.ts and uses state properties updateModalState (closed/confirm/checking/result), upstreamDivergence, and mergeModel on the app component. A reference copy of the modal source is kept at skills/safe-merge-update/update-modal.ts.
Button States
- N Updates (accent-colored): Git upstream has N newer commits
- Updates Available (accent-colored): npm registry has a newer version (non-fork workflows)
- ✓ Up to Date (muted pill, clickable): Up to date — click still opens the modal to re-check
- Merging… (spinner, disabled): Merge in progress
How Update Detection Works
The gateway runs two parallel checks:
- npm registry (
update-startup.ts): ComparesVERSIONagainst npm latest. Used for standard installs. - Git upstream (
update.checkUpstreamRPC): Runsgit fetch upstream && git rev-list --count HEAD..upstream/main. Used for fork workflows. Result is cached for 5 minutes.
For forks, the git check is authoritative — your local package.json version will often be ahead of npm (since you're building from source), so the npm check would incorrectly say "up to date."
Post-Merge Checklist
After a successful merge, always:
- Run
pnpm ui:build— the Control UI is served fromdist/control-ui/ - Update systemd service version — the UI header reads
OPENCLAW_SERVICE_VERSIONfrom the service unit:NEW_VERSION=$(node -e "console.log(require('./package.json').version)") sed -i "s/OPENCLAW_SERVICE_VERSION=.*/OPENCLAW_SERVICE_VERSION=$NEW_VERSION/" ~/.config/systemd/user/openclaw-gateway.service systemctl --user daemon-reload - Run
openclaw gateway restart— pick up the new build - Check config schema compatibility — upstream may add
.strict()to schemas - Clean up backups:
rm -rf /tmp/safe-merge/— while backups are redacted, remove them when no longer needed
Changelog
2026-03-02 — Fully Automated Pipeline
- Added
scripts/safe-merge-update.sh— end-to-end automated merge pipeline - Source changed from
origin/main→upstream/main(now pulls real OpenClaw updates) - Fork branch updated to
local-desktop-main(wasmain) - Conflicts auto-resolved by Claude using baked-in strategy (no human needed for happy path)
- Auto-prunes stale
safe-merge-*branches on each run (prevents infinite accumulation) - On success: pushes to
myfork/local-desktop-main, deletes temp branch, restarts gateway - On build failure: leaves safe-merge branch intact, exits cleanly for investigation
--resumeflag: skip merge, jump straight to build → push → cleanup (for post-manual-fix)- MERGE_MANIFEST.json v1.2.0: added
hostinger.ts,plugins-ui.ts,memory.tsas protected files - Memory tab now shows spinner on first load (no more flash of empty content)
2026-03-01 — Background Sessions Panel
- Added
bgSessions.listandbgSessions.historyRPC handlers - Added Background Sessions Panel UI (right-side drawer, session selector, live transcript, send input)
- Clicking the updates badge now opens the panel alongside the update modal
- State fields:
bgSessionsPanelOpen,bgSessionsList,bgSessionsSelectedKey,bgSessionsHistory,bgSessionsInput,bgSessionsSending,bgSessionsHistoryLoading,bgSessionsLoading - MERGE_MANIFEST.json updated with 3 new protected files and
mustPreservepatterns
2026-03-01 — Merge of 148 upstream commits
Conflicts resolved in: app-render.helpers.ts, app-render.ts, app-view-state.ts, app.ts. Key resolutions:
app-render.helpers.ts: keptrenderContextGauge, mergedhideCron+sessionsHideCronfrom upstream, mergedcountHiddenCronSessions, preservedrenderRecentArchivedOptionsapp-render.ts: kept our nav imports (getDynamicTabGroups, Jarvis/mode/usage); added upstream'sresolveConfiguredCronModelSuggestionsapp-view-state.ts: keptsessionsAgentFilter+ addedsessionsHideCronfrom upstreamapp.ts: kept session history fields + addedsessionsHideCron = truedefault from upstream
Known Issues / Lessons Learned
Discord Voice Schema (2026-02-27)
Upstream added .strict() to DiscordVoiceSchema, rejecting keys our fork previously supported. Fix: remove unsupported keys from config, or add them back to the schema.
Control UI Assets Missing (2026-02-27)
pnpm build builds the gateway but NOT the Control UI. UI needs separate pnpm ui:build. The validate script now includes this.
Duplicate Schema Properties (2026-02-27)
Git auto-merge kept both our extracted const AND upstream's inline block. Fix: manual dedup during AI conflict resolution.
CSP connect-src Extensions (2026-03-01)
Our fork adds http://localhost:* (Jarvis voice agent) and https://api.openai.com (Realtime API) to the CSP connect-src directive in control-ui-csp.ts. Upstream only has ws: wss:. On merge, always keep our extensions — they're required for voice features.
Branch Naming Convention (2026-03-01)
Merge branches should be date-stamped: safe-merge-YYYY-MM-DD. This makes it easy to identify merge attempts and clean up old branches.
Auto-Merged Protected Files Need Verification (2026-03-01)
Even files that auto-merge without conflicts can lose custom code if upstream refactors the surrounding context. After merge, always verify mustPreserve patterns exist in auto-merged protected files — don't just trust git's auto-merge.
pnpm install May Add Packages (2026-03-01)
Upstream may add new dependencies. When 162 commits are merged, pnpm install downloaded 51 new packages. This is expected but worth noting in the merge report.
Safety Summary
- ✅ Backups before any edits —
/tmp/safe-merge/backups/ - ✅ New branch — never merges directly into current branch
- ✅ Validation must pass before committing
- ✅ No external downloads in skill setup — skill files are all local; merge workflow does use network for
git fetchandpnpm install - ✅ No credential requirements — uses your existing agent model
- ✅ Secrets redacted before model transmission — API keys, tokens, passwords, private keys
- ✅ Only conflict diffs sent to model — not the entire repo
- ✅ User controls merge — agent reports results, user decides to push
- ✅ Dry-run in worktree — pre-flight uses a temp worktree, not your working tree
- ✅ Stops on failure — if validation fails, reports and stops rather than pushing broken code
Reviews (0)
No reviews yet. Be the first to review!
Comments (0)
No comments yet. Be the first to share your thoughts!