Upgrading a project
A scaffolder gives you code once and then watches it rot. Grit keeps fixing the project it generated: grit upgrade brings an existing app up to the current templates, and it knows which files you have edited, so it never overwrites your work to do it.
The two commands people confuse
grit update # updates the CLI binary on your machinegrit upgrade # run inside a project: brings THAT project up to the CLI's version
They are different commands and the error from using the wrong one is confusing, so it is worth reading twice. grit upgrade is the one that touches your code.
It knows what you have edited
Grit records a hash of every file it writes, in .grit/manifest.json. That gives three answers for any generated file, and they lead to three different behaviours:
- unchanged: the bytes on disk are the bytes Grit wrote, so it is safe to replace. It gets the new version.
- modified: you have edited it. The upgrade reports a conflict and leaves your file alone. Nothing you wrote is lost by running this command.
- untracked: Grit never wrote it. It is yours and is not touched.
That is why the manifest exists, and it is also why an agent working in your project should read it before proposing an edit: changing a pristine file is the moment that file stops receiving framework fixes. grit mcp exposes it as grit_file_ownership.
grit upgrade --diff # show what it would change in the files it leaves alonegrit upgrade # do itgrit upgrade --plan # write a runbook for the files it left alonegrit upgrade --force # overwrite everything, including your edits
The part that needs judgment
Leaving an edited file alone is a protection, and it is also a limit. What the new version would have changed in that file goes unapplied, and before v3.359.0 it was reported as one line of terminal output that scrolls away.
There is no mechanical answer: the reason the file was skipped is that applying the change needs judgment about code you wrote. But a change that needs judgment is one a coding agent can carry out when it is told what the change is, where to look and how to check the result.
grit upgrade --plan writes UPGRADE-PLAN.md at your project root: which version you came from, every file left alone, the diff of what the new version does to it, and a command whose success means it worked. An instruction with no verification step is one an agent will report as done whether or not it is, so every entry has all three.
### 1. `docker-compose.yml`**Detect.** Open it, and see what you changed:git diff HEAD -- docker-compose.yml**Change.** This is what the new version does to it:- restart: unless-stopped+ container_name: shop-postgres+ restart: unless-stopped**Verify.**docker compose -f docker-compose.yml config > /dev/null
The diff is what Grit would have written against what you have. It is not a patch to apply blindly: your edits are in there for a reason, and the job is to carry the new behaviour into them rather than to replace one with the other.
The files you own, that Grit also writes into
Some files are genuinely shared. routes.go carries your routes and Grit's, main.go wires your services and Grit's, config.go holds your settings and Grit's. Replacing those would throw away your application; leaving them alone would mean a framework fix never reaches the file where it matters.
So the upgrade carries 53 repairs. A repair finds the exact text Grit wrote, replaces it with the exact text Grit writes today, and when the text is not what Grit wrote, says so and changes nothing:
✓ apps/api/internal/routes/routes.go: /api/health answers ok, degraded, off orunknown per component, reports storage, and merges anything health.Register was given⚠ the verification email is not sent the way Grit wrote it: send it with dispatchMailrather than from a `go` statement, so it is queued, retried and survives a restart
The warning is the interesting half. Grit found code it did not write, so it did not touch it, and it told you what the fix would have been and why. You can take it or leave it.
What travels, and why that is a decision
Two sets of files are rewritten on every upgrade rather than only at scaffold time:
- Framework-owned code: the webhook cluster, feature flags, the permission engine, the audit services, the health package. Nothing describes them in a resource definition and they only make sense as a set. A fix to one that did not reach existing projects would be a fix for new projects only.
- The codegen runtime: the packages generated code imports, such as
internal/paginateandinternal/export. The generator always emits against the CLI's current templates, so a project whose paginate package is six versions old would get a handler referencing a field its own copy of the struct does not declare, and the API would stop compiling with nothing in the error to point at the cause.
Your models, your services, your handlers and your frontend code are not in either set and are never rewritten.
What to do after
cd apps/api && go mod tidy # if the upgrade added a dependencypnpm install # if it added a frontend onegrit doctor # the audit, which is where a new check shows upgrit test # the project's own suites
The upgrade tells you which of these it needs. grit doctor is worth running either way: a release often adds a check before it adds a fix, and the check is how you find out the fix applies to you.
How often
Grit releases often, and the version number is deliberately boring about it: a minor never breaks a running application. Upgrading is a thing to do when you want something from the changelog, not a thing to keep up with. A project three months behind upgrades in one command, because every repair checks its own preconditions and the ones that do not apply do nothing.
