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 --force # overwrite everything, including your edits
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.
