diff --git a/docs/README.md b/docs/README.md index 28bdc68dc5..b5cdfdfdc6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 | diff --git a/docs/SELF_UPDATE.md b/docs/SELF_UPDATE.md index 0998abfd2b..3b407df194 100644 --- a/docs/SELF_UPDATE.md +++ b/docs/SELF_UPDATE.md @@ -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 && 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.