Pulling in GlassKit updates
How to merge new boilerplate versions into your fork without losing your customizations.
When upstream changes land in
GlassKitApp/glasskit-boilerplate, you pull them into your fork
on your own cadence. This page covers the mechanics.
The merge is plain git: no custom CLI, no release manager.
Convex schema migrations get a short playbook at the bottom for
the rare cases where the boilerplate changes the users (or
future) table shape.
One-time setup: track the upstream
Inside your repo, add the boilerplate as a second remote:
git remote add upstream https://github.com/GlassKitApp/glasskit-boilerplate
git fetch upstream
git remote -v
# origin https://github.com/YOU/YOUR_FORK (push/fetch)
# upstream https://github.com/GlassKitApp/glasskit-boilerplate (push/fetch)If GitHub asks for credentials, sign in with the account that
accepted the original invite. Your read access to the private
boilerplate is what unlocks fetch upstream.
Pulling updates
Periodically:
git fetch upstream
git log --oneline HEAD..upstream/main # what's new?
git merge upstream/main # or rebase, your callgit merge is the safer default (it makes the upstream merge a
single commit you can revert cleanly). git rebase upstream/main
keeps history linear if you prefer that.
Resolve conflicts (see patterns below), then:
pnpm install # in case lockfile changed
cd packages/backend && pnpm exec convex dev # if schema changed, regenerate types
git push origin mainCommon conflict patterns
The boilerplate is opinionated about where customizations land, so most updates merge cleanly. The conflicts that DO happen fall into four buckets:
pnpm-lock.yaml
Always resolve in favor of the upstream version, then re-run
pnpm install to reconcile against your package.json:
git checkout --theirs pnpm-lock.yaml
pnpm install
git add pnpm-lock.yaml.env.example (any of the three)
Take both sides: keep the new keys from upstream + keep any custom keys you added. Manually merge with your editor; this is short.
packages/backend/convex/schema.ts + users.ts
If upstream changes the users table or adds a new table, you'll
get a conflict on the affected files. See the Schema migrations
section below.
Marketing / branding files you customized
companion/app/page.tsx, companion/app/globals.css,
app/src/App.tsx (launcher): these are the files you most often
edit for your own product. Conflicts here mean upstream improved
the example while you replaced it with yours.
Default rule: keep yours
git checkout --ours <file> to discard the upstream changes for
these. Then read the upstream diff manually with
git show upstream/main -- <file> and cherry-pick anything
actually useful (a new helper component, a better
keyboard-shortcut handler, etc.).
Versioning expectations
There is no formal version number on the boilerplate today; the repo's commit history is the changelog. Subscribe to the repo on GitHub (Watch → Custom → Releases when we start tagging) so you hear about updates.
What stays stable
- Public component APIs in
@glasskit/glasses-ui(signatures of<GlassViewport>,useDpad, the sensor hooks): breaking changes here get a major version bump and a migration note in the commit body - The shape of
users.ts(we add fields, we don't remove or rename in place) - The Convex env-var contract (we add keys, we don't repurpose them)
- Stripe component conventions: we may add tiers but the
existing
PRICE_TO_PLANmapping stays valid
What's allowed to change without notice
- Internal helper functions inside
packages/backend/convex/lib/ - Demo apps in
app/src/apps/: you fork these and rename, so upstream replacing or restyling a demo doesn't affect your fork unless you didn't fork - Marketing copy in the companion's landing pages:
companion/app/page.tsx,companion/components/landing/*. You customized these on day one; upstream changes here are reference, not prescription - Docs (this site)
Schema migrations
Convex schemas are validated at deploy. If upstream adds a required field to an existing table, your existing rows won't satisfy the new schema. Two-step migration pattern.
When upstream adds a required field
Pull the schema change first, but keep the field optional for one deploy:
users: defineTable({
clerkId: v.string(),
email: v.optional(v.string()),
name: v.optional(v.string()),
newField: v.optional(v.string()), // upstream said required; you defer
})Backfill existing rows via a Convex internal mutation you write once:
export const backfillNewField = internalMutation({
args: {},
handler: async (ctx) => {
const users = await ctx.db.query("users").collect();
for (const u of users) {
if (u.newField === undefined) {
await ctx.db.patch(u._id, { newField: "default-value" });
}
}
},
});Run via pnpm exec convex run users:backfillNewField from
packages/backend/.
Tighten the schema to match upstream once backfill is done:
newField: v.string(),When upstream removes a field
You don't have to remove it from your fork; Convex tolerates
unused fields. But to keep merges clean, remove it from your
schema.ts and let any code that referenced it surface a
TypeScript error so you remove the reference too.
Staying on a frozen version
If you've shipped to production and want to defer updates until a specific milestone:
git tag frozen-2026-Q2 # mark your current state
git push --tags originNow your fork stays where it is until you git merge upstream/main
on your own schedule. The customer channels in our Discord
(#feedback) are where we announce breaking changes ahead of
merging them upstream so you can plan the merge.
When you can't merge cleanly
Ask for help
DM the founder on X (@JarJarMadeIt) or post in the customer
Discord's #code-questions. If the conflict is structural
(upstream re-architected something you can't cleanly merge), we
can usually walk through it together.