Skip to content

Updating from base

A licensee fork is not a one-shot purchase. Every time the base (ddakit/saas-base) adds a new skill, fixes a hook, or updates the domain rules in AI_AUTOMATION.md, those changes need a way back to your fork. Otherwise the value of the license stops at the day you bought it. This page covers the assumptions behind /update-from-base, the skill that lets you pull those changes in without knowing git rebase.

The core split is three-way: what gets overwritten automatically, what is never touched, and what asks before doing anything.

Whether an update overwrites a file is decided by where the file lives.

The base is the source of truth here. Any local edits get reverted on the next sync.

PathWhat
.claude/skills/{base-skill-name}/Base skill definitions (phase 1 through 6, implement-*, review-saas, test-saas, deploy-saas, /update-from-base, etc.)
.claude/agents/*.mdBase agent definitions (saas-architect, saas-builder, saas-reviewer)
.claude/hooks/*.sh plus .claude/hooks/README.mdBase hooks (session-start, stop-reminder, pre-commit-check)
.claude/settings.jsonBase hook registration
AI_AUTOMATION.mdDomain SSOT
AGENTS.mdCodex entry point

Don’t edit base skill files in place. If you need a new skill, add it under your own directory (for example .claude/skills/my-team-{name}/). When the base ships new skills into base-owned territory, your own directories are untouched.

PathWhat
src/Your SPA source
api/functions/, api/migrations/, api/policies.test.sql, api/seed.sqlYour server code, migrations, RLS policies, RLS tests
package.jsonYour dependencies
vite.config.ts, tsconfig.jsonYour build setup
index.htmlCSP, og meta, per-domain content
public/favicon, robots.txt, sitemap.xml
docs/design/saas-spec.mdYour spec
docs/adr/*.md (excluding README.md)Your decision records
docs/deployment/, docs/prompts/, docs/ai-rules/Your output
.claude/state/*, .claude/session-state/*, .claude/agent-memory/*Runtime, session, memory
.env*Environment variables
LICENSE, COMMERCIAL-LICENSE.mdLicenses

License files are not auto-overwritten even if the base ships an update. The tool flags the change and leaves the decision to you.

.env* files sit outside the auto-apply area as well. If the base adds a new slot to .env.example, the tool walks you through filling that variable into your own .env.local.

CLAUDE.md and README.md. The base writes here and so do you, so the tool shows the diff and asks instead of applying.

Three options:

  1. Take the base version wholesale. Your edits are gone.
  2. Three-way merge with git merge-file. Resolve conflicts by hand if any appear.
  3. Defer. The same prompt comes back next time.

README.md gets rewritten by phase 6 (/6-cleanup-residue) with your SaaS name and description, so overwriting it wholesale with the base version loses your SaaS name. That is why it sits in mixed.

Register the base as a git remote.

Terminal window
git remote add base https://github.com/ddakit/saas-base.git
git remote -v

git remote -v should show two base lines as fetch URLs. You don’t need to run this manually; the first call to /update-from-base walks you through it if the remote is missing.

State lives in .claude/state/base-upstream.json.

{
"remote": "base",
"branch": "main",
"last_synced_commit": "<hash>",
"last_synced_at": "<ISO>"
}

The tool only looks at commits between last_synced_commit and base/main, so once this is filled in the first time the rest is automatic.

Calling /update-from-base (or saying “pull from base” in plain language) runs through the following.

  1. git status --porcelain to check for uncommitted work. If dirty, the tool offers git stash.
  2. git fetch base main.
  3. git diff --name-status <last_synced_commit>..base/main to bucket each changed file by ownership.
  4. Print a base-owned summary: how many new skills, how many hook updates, whether AI_AUTOMATION.md moved.
  5. On yes, git checkout base/main -- .claude/skills/ .claude/agents/ .claude/hooks/ .claude/settings.json AI_AUTOMATION.md AGENTS.md.
  6. Walk through mixed files one at a time and pick option 1, 2, or 3.
  7. Post-apply checks: chmod +x on .sh files, JSON parse on settings.json, list trigger keywords for any new skills.
  8. Update .claude/state/base-upstream.json.
  9. Suggest a commit. Default message: chore(base): sync to {short-hash} ({N} files).

A typical cycle finishes in 30 seconds to two minutes. The exception is a large mixed-file diff that needs careful reading.

Even if the base adds a new RLS policy example to AI_AUTOMATION.md section 6.6, that policy does not auto-land in your api/migrations/. Policy shape depends on your domain entities.

The base is the SSOT for “where the policies live and how they are written,” not “which policies you need.” The latter is your decision, and it gets filled in during the phase 4 data-schema decision and the phase 5 implement-data-layer step.

If your git history shows commits touching .claude/skills/{base-skill}/, the tool reclassifies that file as mixed instead of base-owned. The auto-overwrite path would discard your work.

The cleaner pattern is to leave base skills alone and add a parallel skill in your own directory with the same trigger keywords. Both skills coexist; the manual dispatches to yours by name. When the base updates its skill, yours stays put.

If last_synced_commit no longer exists in base/main (the base history was rewritten), the tool stops and only prints a notice. This is rare. It happens when the base maintainer had to remove a sensitive commit or a license-violating piece of content.

Recovery is manual.

  1. Move user-owned areas (src/, api/, docs/design/, docs/adr/, package.json, index.html, .env*) onto a working branch.
  2. Reset to base/main and rebuild on top of the new base.
  3. Cherry-pick or copy your user-owned work back in.

This is not the territory /recover-from-blocked covers. If a force-push actually lands, the base maintainer publishes a separate notice with the recovery steps.

The simplest design would be a single git pull base main. Two reasons that wasn’t the choice.

A licensee builds on top of a fixed snapshot of the base. git pull treats base changes and your changes as the same layer and produces a merge commit. For non-developer users, that merge commit and any conflicts that follow it are exactly the wall they hit.

The base-owned area carries an implicit contract: the base is the SSOT for these files. That contract makes auto-overwrite safe. Files without that contract (mixed) are left to the human. If the fork cannot evolve, the base’s value freezes the day you bought it. If the fork pulls indiscriminately, your edits vanish. /update-from-base is the channel between those two failure modes.

  • The same flow paced for non-developers lives in the Beginner manual update page.
  • The reasoning behind why this skill is a safety net for both the base thesis and the license model sits next to the “What is missing right now” paragraph in intro.