Pulling base updates
A licensee fork isn’t a one-time grab. Every time the base (ddakit/blog-studio) adds a new skill, fixes a hook, or updates the domain rules in AI_AUTOMATION.md or the Blogger publish script, that change has to reach the fork for the license value to hold. /update-from-base is the spot that lets you receive it safely without knowing git rebase.
Auto-applicable, never touched, decided by a human. The fork between these three is the heart of it.
Owner by area
Section titled “Owner by area”What an update overwrites and what it leaves alone is decided by file location.
base-owned — auto-updated
Section titled “base-owned — auto-updated”| Path | What |
|---|---|
.claude/skills/{base-skill-name}/ | base skills (start, blog-orchestrator, the 13 writing-track + 6 marketing-track skills) |
.claude/agents/*.md | the 6 agents (strategy, production, quality + marketing-director, content-lead, seo-discovery-lead) |
.claude/hooks/*.sh, .claude/settings.json | base hooks and their registration |
AI_AUTOMATION.md | the domain SSOT (Phase definitions, output contract, AI gate categories, security S1–S8) |
scripts/deploy_to_blogger.py, scripts/deploy.sh, scripts/requirements.txt | Blogger publish scripts |
.env.example, .gitignore | placeholder and standard |
The rule is not to edit base skill bodies directly. To add your own skill, make it in a separate directory. Even if a new skill gets added to the base-owned area, your own directory isn’t affected.
user-owned — never touched
Section titled “user-owned — never touched”| Path | What |
|---|---|
_workspace/posts/, _workspace/pages/, _workspace/briefs/ | drafts in progress, the publish archive, per-stage briefs, images |
.env | BLOG_ID_KO, BLOG_ID_EN |
scripts/credentials.json, scripts/token.json | Google OAuth (not git-tracked) |
.claude/agent-memory/, .claude/state/, .claude/session-state/ | memory, runtime, session handoff |
LICENSE, COMMERCIAL-LICENSE.md | license |
License files and .env aren’t overwritten automatically even when the base updates them. If the base added a new variable slot to .env.example, it only guides you to fill that variable into your own .env.
mixed — 3-way guidance
Section titled “mixed — 3-way guidance”CLAUDE.md, AGENTS.md, README.md — three. Because edits from both the base and the user can land in them, they aren’t auto-updated; it shows the diff and asks. README.md is the spot /start rewrites with your blog’s name and identity, so overwriting the base change wholesale would erase your blog identity.
.claude/product-marketing-context.md is one exception. It’s the blog identity the fork user filled in, so normally it’s user-owned, but if the base updates the placeholder structure itself, it gets demoted to mixed for guidance.
One-time setup
Section titled “One-time setup”Register the base as a git remote. The first call guides this step automatically, so there’s no need to type it ahead of time.
git remote add base https://github.com/ddakit/blog-studio.gitgit remote -vThe state file goes in .claude/state/base-upstream.json, and it looks only at the changes from last_synced_commit to base/main.
One cycle
Section titled “One cycle”Calling /update-from-base or “pull base updates” goes through the following.
- Check for dirty with
git status --porcelain. If there are unsaved changes, suggestgit stash git fetch base main- Classify changed files by area with
git diff --name-status <last_synced_commit>..base/main - Summarize the base-owned changes. N new skills, M updated hooks, whether
AI_AUTOMATION.mdchanged, whether the publish scripts changed - On a user yes, apply only the base-owned paths with
git checkout base/main -- - Handle mixed files one at a time, choosing among three options (overwrite wholesale, 3-way merge, defer)
- Verify after applying.
chmod +xon.sh, parsesettings.jsonJSON, list the new skill’s trigger keywords - Update
.claude/state/base-upstream.json - Suggest a commit. The message is
chore(base): sync to {short-hash}
Destructive commands aren’t run automatically per S5; they get user confirmation. The default flow finishes in 30 seconds to 2 minutes, with one branch where the mixed decisions take longer.
Why the publish script is base-owned
Section titled “Why the publish script is base-owned”deploy_to_blogger.py is the spot that enforces the AI-smell and quality gates at the code level (S8). If a user modifies this script, the gate could be neutralized, so the base holds it as the SSOT. To change publishing behavior, adjust it where it follows the gate thresholds in AI_AUTOMATION.md rather than editing the script. A republish that has to bypass the gate is opened only with the --skip-gate flag.
When there’s a trace of you editing a base skill
Section titled “When there’s a trace of you editing a base skill”If you’ve ever modified a file under .claude/skills/{base-skill}/ in a commit (detected via git log), that file is treated as mixed rather than base-owned, because auto-overwriting would erase your work. To avoid this, don’t edit the base skill directly; make a separate skill with the same trigger keywords in your own directory.
Adjacent spot
Section titled “Adjacent spot”The same procedure followed at a beginner pace is in beginner updates.