GlassKit

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 call

git 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 main

Common 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_PLAN mapping 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:

packages/backend/convex/schema.ts (transitional)
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:

packages/backend/convex/users.ts
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:

packages/backend/convex/schema.ts
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 origin

Now 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.

On this page