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.
Area ownership
Section titled “Area ownership”Whether an update overwrites a file is decided by where the file lives.
base-owned — auto-applied
Section titled “base-owned — auto-applied”The base is the source of truth here. Any local edits get reverted on the next sync.
| Path | What |
|---|---|
.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/*.md | Base agent definitions (saas-architect, saas-builder, saas-reviewer) |
.claude/hooks/*.sh plus .claude/hooks/README.md | Base hooks (session-start, stop-reminder, pre-commit-check) |
.claude/settings.json | Base hook registration |
AI_AUTOMATION.md | Domain SSOT |
AGENTS.md | Codex 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.
user-owned — never overwritten
Section titled “user-owned — never overwritten”| Path | What |
|---|---|
src/ | Your SPA source |
api/functions/, api/migrations/, api/policies.test.sql, api/seed.sql | Your server code, migrations, RLS policies, RLS tests |
package.json | Your dependencies |
vite.config.ts, tsconfig.json | Your build setup |
index.html | CSP, og meta, per-domain content |
public/ | favicon, robots.txt, sitemap.xml |
docs/design/saas-spec.md | Your 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.md | Licenses |
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.
mixed — three-way prompt
Section titled “mixed — three-way prompt”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:
- Take the base version wholesale. Your edits are gone.
- Three-way merge with
git merge-file. Resolve conflicts by hand if any appear. - 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.
One-time setup
Section titled “One-time setup”Register the base as a git remote.
git remote add base https://github.com/ddakit/saas-base.gitgit remote -vgit 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.
One cycle
Section titled “One cycle”Calling /update-from-base (or saying “pull from base” in plain language) runs through the following.
git status --porcelainto check for uncommitted work. If dirty, the tool offersgit stash.git fetch base main.git diff --name-status <last_synced_commit>..base/mainto bucket each changed file by ownership.- Print a base-owned summary: how many new skills, how many hook updates, whether
AI_AUTOMATION.mdmoved. - On
yes,git checkout base/main -- .claude/skills/ .claude/agents/ .claude/hooks/ .claude/settings.json AI_AUTOMATION.md AGENTS.md. - Walk through mixed files one at a time and pick option 1, 2, or 3.
- Post-apply checks:
chmod +xon.shfiles, JSON parse onsettings.json, list trigger keywords for any new skills. - Update
.claude/state/base-upstream.json. - 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.
RLS migrations stay user-owned
Section titled “RLS migrations stay user-owned”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.
When you’ve edited a base skill
Section titled “When you’ve edited a base skill”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.
When the base force-pushes
Section titled “When the base force-pushes”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.
- Move user-owned areas (
src/,api/,docs/design/,docs/adr/,package.json,index.html,.env*) onto a working branch. - Reset to
base/mainand rebuild on top of the new base. - 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.
Why area split instead of one auto-pull
Section titled “Why area split instead of one auto-pull”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.
Adjacent
Section titled “Adjacent”- 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.