Debugging Blog Automation: 7 Fixes in 24 Hours (Git Subtree Architecture)
Debugging Blog Automation: 7 Fixes in 24 Hours (Git Subtree Architecture)
Meta Description: Fixed blog auto-publishing in 24 hours: YAML syntax errors, Git Subtree sync, GitHub Actions permissions. Here’s what broke and how we fixed it.
Our blog auto-publishing stopped working. Posts with published: false weren’t changing to published: true on their scheduled dates. Worse, when we tried to fix it, we discovered the entire architecture was broken.
Here’s the 24-hour debugging journey that resulted in 7 fixes, a complete Git Subtree implementation, and a working automated blog workflow.
The Problem: Auto-Publish Wasn’t Running
Symptom: Blog posts dated for past dates remained unpublished.
Expected Behavior:
- GitHub Actions workflow runs daily at 14:00 UTC
- Finds posts with
date: 2026-01-10andpublished: false - Changes
published: false→published: true - Commits changes
- Blog deploys automatically
Reality: Workflow failed with YAML syntax error on line 152.
Fix #1: YAML Syntax Error (5 minutes)
Error:
error: You have an error in your yaml syntax on line 152
Root Cause:
# BROKEN - Variable expansion with newlines broke YAML
COMMIT_MSG="Auto-publish: Daily blog post(s) for $TODAY
Published $POST_COUNT post(s) scheduled for $(date -u +'%B %d, %Y'):
${PUBLISHED_TITLES} # ← This variable contained newlines!
🤖 Automated by GitHub Actions"
Fix:
# FIXED - Simplified commit message
git commit -m "Auto-publish: Daily blog post(s) for $TODAY
Published $POST_COUNT post(s) scheduled for $(date -u +'%B %d, %Y')
🤖 Automated by GitHub Actions"
Lesson: Bash variable expansion with newlines inside YAML multiline strings is fragile. Keep it simple.
Commit: 57e9568
Fix #2: Build Error - Duplicate Export (10 minutes)
After fixing the YAML error, deployment failed with:
Error:
ERROR: Multiple exports with the same name "collections"
ERROR: The symbol "collections" has already been declared
Root Cause:
// config.ts had TWO exports:
export const collections = { blog }; // Line 17
// ... later ...
export const collections = { // Line 30
blog: blogCollection,
course: courseCollection,
};
Fix: Removed the duplicate first export, kept the combined one.
Commit: 89aa85f
Lesson: When refactoring, grep for duplicate exports before committing.
Fix #3: The Architecture Problem (2 hours)
Now the workflow ran successfully, but posts still didn’t appear on the blog.
Discovery: The blog exists in two separate repositories:
alienbraintrust-private- Where we write postslabs-journey-blog- Where the blog deploys from
The auto-publish workflow was running in the private repo, changing published: false → true there, but the deployment repo never received the changes.
Original (Broken) Flow:
Write post in private repo
↓
Auto-publish changes published status in private repo
↓
❌ Deployment repo has old version
↓
❌ Blog doesn't update
Decision Point: Three options:
- Git Subtree (private repo = source of truth)
- Dual repos (manual sync)
- Monorepo (merge everything)
Choice: Option 1 - Git Subtree (matches original architecture intent).
Fix #4: Git Subtree Sync Workflow (1 hour)
Created: .github/workflows/sync-blog-to-deployment.yml
# Triggers after auto-publish completes
workflow_run:
workflows: ["Auto-Publish Daily Blog Posts"]
types: [completed]
# Uses git subtree to sync blog directory
git subtree push --prefix="03-Marketing Materials/labs-journey-blog" \
blog-deploy main
Why Git Subtree?
- Keeps all content in private repo (source of truth)
- Auto-syncs to deployment repo
- Preserves git history
- No manual copy/paste between repos
Commit: 6dcf151
Fix #5: Permission Denied (30 minutes)
Error:
remote: Permission to base-bit/labs-journey-blog.git denied to github-actions[bot]
fatal: unable to access 'https://github.com/base-bit/labs-journey-blog.git/':
The requested URL returned error: 403
Root Cause: The default GITHUB_TOKEN only has permissions for the current repository. Cross-repo pushes require a Personal Access Token (PAT).
Fix:
- Created GitHub PAT with
reposcope - Added as
GH_PATsecret in repository settings - Updated workflow to use
secrets.GH_PATinstead ofsecrets.GITHUB_TOKEN
Commit: 5043eb1
Lesson: Cross-repository GitHub Actions need PATs, not the default token.
Fix #6: Non-Fast-Forward Push (20 minutes)
Error:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs
hint: Updates were rejected because the tip of your current branch is behind
Root Cause: The deployment repo had diverged from the private repo (we’d made direct edits there while debugging).
Fix: Added force push fallback:
# Try normal push first
if git push blog-deploy $SPLIT_SHA:main; then
echo "✅ Normal push successful"
else
echo "⚠️ Trying force push..."
git push blog-deploy $SPLIT_SHA:main --force
fi
Commit: 1da5ed1
Lesson: Initial subtree syncs often need force push. After that, everything is fast-forward.
Fix #7: Workflow Scope Permission (15 minutes)
Error:
! [remote rejected] main (refusing to allow a Personal Access Token to
create or update workflow `.github/workflows/auto-publish.yml`
without `workflow` scope)
Root Cause: The blog directory contained its own copy of the auto-publish workflow. GitHub requires the workflow scope to push workflow files, but we only had repo scope.
Fix: Removed the duplicate workflow from the blog directory. The auto-publish workflow should only exist in the parent repo.
Before:
alienbraintrust-private/
├── .github/workflows/auto-publish-blog.yml
└── 03-Marketing Materials/labs-journey-blog/
└── .github/workflows/auto-publish.yml # ← Duplicate!
After:
alienbraintrust-private/
├── .github/workflows/auto-publish-blog.yml # ← Only here
└── 03-Marketing Materials/labs-journey-blog/
└── .github/workflows/deploy.yml # ← Deployment only
Commit: ec2bfbc
Lesson: Keep workflows in the appropriate repo. Don’t sync workflow files to deployment repos.
Final Architecture
Source of Truth: alienbraintrust-private
alienbraintrust-private (WRITE HERE)
├── .github/workflows/
│ ├── auto-publish-blog.yml ← Publishes posts daily at 14:00 UTC
│ └── sync-blog-to-deployment.yml ← Syncs via git subtree
└── 03-Marketing Materials/labs-journey-blog/
└── src/content/blog/*.md ← Write posts here
↓ Git Subtree Push ↓
labs-journey-blog (DEPLOYMENT ONLY)
├── .github/workflows/
│ └── deploy.yml ← Deploys to GitHub Pages
└── src/content/blog/*.md ← Auto-synced from private
↓ GitHub Pages ↓
https://blog.alienbraintrust.ai/
Workflow Now
Daily Auto-Publish (Scheduled):
- 14:00 UTC: Auto-publish workflow checks for posts dated today
- Changes
published: false→published: true - Commits to private repo
- Sync workflow triggers
- Git subtree pushes blog directory to deployment repo
- Deployment repo auto-deploys to GitHub Pages
- Blog updates automatically
Manual Publish (Immediate):
# Option A: Change published status
# Edit .md file: published: false → published: true
git commit -m "Publish: Post Title"
git push # Sync workflow triggers automatically
# Option B: Trigger auto-publish manually
# Visit GitHub Actions and click "Run workflow"
Debugging Metrics
Total Time: 24 hours (intermittent work) Total Fixes: 7 Commits: 7 (each fixing one issue) Lines of Code Changed: ~500 Documentation Created: 3 guides (800+ lines)
Breakdown:
- YAML syntax error: 5 min
- Build error: 10 min
- Architecture research: 2 hours
- Git Subtree implementation: 1 hour
- Permission issues: 30 min
- Force push handling: 20 min
- Workflow scope: 15 min
- Documentation: 2 hours
- Testing/verification: 1 hour
Key Takeaways
1. Multi-Repo Architectures Need Clear Workflows
Problem: Having the blog in two repos caused confusion about which was authoritative.
Solution: Document the architecture explicitly. Create a clear “source of truth” policy.
Files Created:
BLOG-WORKFLOW-ARCHITECTURE.md- Explains the two-repo setupBLOG-SETUP-GUIDE.md- Step-by-step workflowSETUP-GITHUB-PAT.md- Token configuration
2. Git Subtree vs Submodule
Git Subtree:
- ✅ Simpler for contributors (just one repo to clone)
- ✅ Can commit directly to subtree
- ✅ No .gitmodules complexity
- ❌ Force push needed on first sync
Git Submodule:
- ✅ Cleaner separation
- ✅ Each repo maintains its own history
- ❌ More complex (nested repos)
- ❌ Easy to forget
git submodule update
Our Choice: Subtree - simpler for a blog use case.
3. GitHub Actions Cross-Repo Permissions
Default GITHUB_TOKEN:
- ✅ Works for current repo
- ❌ Can’t push to other repos
- ❌ Can’t trigger workflows in other repos
Personal Access Token (PAT):
- ✅ Works across repos
- ✅ Can push/pull/trigger workflows
- ⚠️ Needs proper scopes (
repo, optionallyworkflow) - ⚠️ Rotate regularly (90 days recommended)
4. Debugging Multi-Step Workflows
Strategy:
- Identify the failure point (YAML error, build error, permission error)
- Fix one thing at a time (commit after each fix)
- Test incrementally (don’t fix 3 things and hope it works)
- Document as you go (future you will thank you)
Anti-pattern: Trying to fix multiple issues in one commit. When it fails, you don’t know which fix was wrong.
5. Automation Doesn’t Mean “Set and Forget”
Even automated workflows need:
- Monitoring (check if they run successfully)
- Error handling (what happens when the workflow fails?)
- Recovery procedures (how to manually trigger if needed)
- Documentation (how does this actually work?)
What’s Next
Immediate:
- Monitor auto-publish workflow for next 7 days
- Verify scheduled posts publish correctly
- Document any edge cases discovered
Future Improvements:
- Add workflow status notifications (Slack/email)
- Implement retry logic for failed syncs
- Add deployment preview for unpublished posts
- Create dashboard showing upcoming scheduled posts
Long-Term:
- Consider consolidating to monorepo (if dual-repo causes more issues)
- Evaluate alternative deployment strategies (Netlify, Vercel)
- Build web UI for managing scheduled posts
Files Created
Workflows:
.github/workflows/sync-blog-to-deployment.yml(102 lines)- Updated
.github/workflows/auto-publish-blog.yml(simplified commit message)
Documentation:
BLOG-WORKFLOW-ARCHITECTURE.md(281 lines)BLOG-SETUP-GUIDE.md(409 lines)SETUP-GITHUB-PAT.md(145 lines)
Skills:
- Updated
.claude/skills/blog-post/SKILL.md(added workflow section)
Total Documentation: ~900 lines
Lessons for AI-Augmented Debugging
What Worked:
- Breaking down the problem into discrete fixes
- Committing after each fix (clear rollback points)
- Testing each fix before moving to the next
- Creating comprehensive documentation
What Didn’t Work:
- Assuming the architecture was simple (it had hidden complexity)
- Not reading the actual error messages carefully enough (saved time later)
- Trying to fix everything at once (caused more confusion)
AI-Specific Insights:
- Claude helped identify the Git Subtree solution quickly
- Documentation generation was faster with AI assistance
- But: Still needed human decision on architecture choice (Option 1 vs 2 vs 3)
Next in series: Part 2 will cover implementing monitoring and alerts for the automated workflows.
Related posts: