Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Index of everything under `docs/`. Start with the [root README](../README.md) fo
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Dev setup (PostgreSQL required), code conventions |
| [GITHUB_ACTIONS.md](./GITHUB_ACTIONS.md) | CI and release workflows |
| [VERSIONING.md](./VERSIONING.md) | SemVer + release process (`/do:release`) |
| [SELF_UPDATE.md](./SELF_UPDATE.md) | Fork-aware self-update flow — release polling, `FORK_SYNC_REQUIRED`, fork sync |
| [SELF_UPDATE.md](./SELF_UPDATE.md) | Fork-aware self-update flow — release polling, `FORK_SYNC_REQUIRED`, fork sync, running a customized fork |
| [MANAGED_APP_UPDATES.md](./MANAGED_APP_UPDATES.md) | Safe managed-app update default and the opt-in app lifecycle contract |
| [DEPS.md](./DEPS.md) | Dependency audit — every third-party package and its verdict |
| [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) | Common runtime issues, known issues |
Expand Down
43 changes: 43 additions & 0 deletions docs/SELF_UPDATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,49 @@ When `isFork` is true, `UpdateTab` replaces the single "Update Now" button with

Keep these three behaviors distinct. Collapsing them strips the user's agency over what touches their GitHub fork.

## Running a customized fork

The `UpdateTab` fork panel states this in short form. The full loop is here, because that panel is
the only other place it exists and it renders only when `isFork` is true.

Keep `main` a clean mirror of upstream and never commit to it — that is what keeps `gh repo sync`
fast-forward and avoids 409 `FORK_DIVERGED`. Private changes live on their own branch, rebased
onto `main` after each sync. Anything shareable goes upstream as a PR instead, so you carry less
forward each time.

**PM2 boots whatever is checked out**, so the branch you are on is the code that runs. Staying on
your private branch is what makes your customizations live; there is no separate step.

### After rebasing, reinstall and rebuild

`update.sh` / `update.ps1` always finish on `main`, so they cannot do this half for you:

```bash
git checkout main && ./update.sh # .\update.ps1 on Windows
git checkout <your-branch> && git rebase main
for d in . client server autofixer; do (cd "$d" && npm install --no-save); done
node scripts/trusted-rebuilds.js server
npm run build && npm run pm2:restart
```

**Use `--no-save`, not a bare `npm install`.** `--no-save` is what `safe_install` in `update.sh`
and `scripts/ensure-deps.js` run, for two reasons that both bite a fork: a bare install rewrites
`client/package.json` and the lockfiles, which dirties your private branch and re-stales a build
you just made (`client/package.json` is a `staleBuild` input — see below); and an older npm can
strip newer lockfile metadata it does not understand. `--no-save` still honors `package-lock.json`.

`scripts/trusted-rebuilds.js` is not optional for the server. Every workspace `.npmrc` sets
`ignore-scripts=true`, so the server's native addons (`node-pty` and friends) are never built by
the install itself — skip the rebuild and the shell and TUI features crash on a missing binding.

Order matters: install before you build, so the build sees the deps it compiles against.

**A clean rebase makes the install look broken.** Checking out a branch based on an older `main`
rewinds the working tree, and the rebase rolls it forward, so every touched file gets a new mtime.
`getInstallState()` (`server/services/installState.js`) compares mtimes, so it reports a stale
build and stale deps in all four workspaces even though no dependency changed and no build input
was edited. The reinstall and rebuild above clear it.

## Image-bearing Persistent Mind work must drain before source transitions

The managed update route refuses to restart into a different source revision while a queued Persistent Mind message or active turn carries image references. `GET /api/update/status` reports the privacy-safe `persistentMindImages` preflight (`safe`, queued count, and active-turn boolean), and `POST /api/update/execute` re-checks it before and after acquiring the update lock.
Expand Down