update-loa
Pull latest Loa framework updates from upstream repository. Fetches, previews, confirms, and merges with conflict guidance. Supports WIP branch testing with checkout option.
/plugin install loa-freesidedetails
Update Loa
Purpose
Pull the latest Loa framework updates from the upstream repository. Safely fetches, previews changes, and merges with guidance for conflict resolution.
Invocation
/update-loa
/update-loa main
/update-loa feature/constructs-multiselect
WIP Branch Testing (v1.2.0)
When a feature branch is specified (matching feature/*, fix/*, topic/*, wip/*, or test/*), the command offers two options via AskUserQuestion:
- Checkout for testing (Recommended) - Creates a local
test/loa-{branch}branch from the remote - Merge into current branch - Traditional merge behavior
Branch Testing Flow
/update-loa feature/constructs-multiselect
↓
AskUserQuestion: "How would you like to use this branch?"
↓
[Checkout] → Creates test/loa-feature/constructs-multiselect
→ Saves state to .loa/branch-testing.json
→ "Ready for testing. Run /update-loa to return."
↓
[Later: /update-loa with no args while in test branch]
↓
AskUserQuestion: "You're testing loa/feature/constructs-multiselect"
↓
[Return to main] → Checks out original branch
→ Clears state file
Configuration
# .loa.config.yaml
update_loa:
branch_testing:
enabled: true
feature_patterns:
- "feature/*"
- "fix/*"
- "topic/*"
- "wip/*"
- "test/*"
test_branch_prefix: "test/loa-"
AskUserQuestion Integration
Branch mode selection (when feature branch detected):
questions:
- question: "How would you like to use branch '{branch}'?"
header: "Branch mode"
options:
- label: "Checkout for testing (Recommended)"
description: "Switch to test/loa-{branch} for isolated testing"
- label: "Merge into current branch"
description: "Merge changes into your current branch ({current})"
multiSelect: false
Return helper (when in test branch and no args):
questions:
- question: "You're testing loa/{branch}. What would you like to do?"
header: "Test branch"
options:
- label: "Return to {original} (Recommended)"
description: "Checkout original branch and clear test state"
- label: "Stay on test branch"
description: "Continue testing, keep state"
- label: "Merge into {original}"
description: "Merge test branch changes into original"
multiSelect: false
State Management
State is tracked via .claude/scripts/branch-state.sh:
# Check if in test mode
.claude/scripts/branch-state.sh is-testing
# Load state
.claude/scripts/branch-state.sh load
# → {"testing_branch":"feature/foo","original_branch":"main",...}
# Clear after return
.claude/scripts/branch-state.sh clear
Prerequisites
- Working tree must be clean (no uncommitted changes)
loaorupstreamremote must be configured- Merge driver configured (one-time):
git config merge.ours.driver true
Workflow
Phase 1: Pre-flight Checks
- Verify working tree is clean
- Verify upstream remote exists
Phase 2: Fetch Updates
git fetch loa main
Phase 3: Show Changes
- Count new commits
- Display commit list
- Show files that will change
Phase 4: Confirm Update
Ask for confirmation before merging. Note which files will be updated vs preserved.
Phase 5: Merge Updates (with --no-commit)
git merge loa/main --no-commit
IMPORTANT: The
--no-commitflag stages the merge without committing, allowing Phases 5.3 and 5.5 to inspect and fix collateral damage before the commit is created. HEAD still points to the pre-merge branch tip during these phases.Conflict handling: If
git merge --no-commitexits non-zero due to conflicts, resolve conflicts first (see Phase 6), then proceed to Phase 5.3. The safeguard operates on staged deletions (--diff-filter=D) which are present even during a conflicted merge state — conflicted files show as "both modified", not as deletions.
Phase 5.3: Collateral Safeguard (v1.3.0, extended v1.40.0)
After the merge is staged but before committing, scan for two categories of collateral damage:
- Deletions (
--diff-filter=D): Files deleted by upstream cleanup that should not propagate - Content replacements (
--diff-filter=M): Project identity files whose content was overwritten by upstream's version during merge conflict resolution
# --- Part 1: Collateral deletion protection (v1.3.0) ---
# Identify files staged for deletion by the merge
deleted_files=$(git diff --cached --diff-filter=D --name-only)
restored_count=0
if [[ -n "$deleted_files" ]]; then
while IFS= read -r file; do
case "$file" in
# Framework zone — upstream deletions are intentional, allow them
.claude/*) ;;
.loa-version.json) ;;
CLAUDE.md) ;;
PROCESS.md) ;;
.gitattributes) ;;
INSTALLATION.md) ;;
.loa.config.yaml.example) ;;
# Everything else — non-framework file, restore from pre-merge state
*)
git checkout HEAD -- "$file" 2>/dev/null && restored_count=$((restored_count + 1)) || true
;;
esac
done <<< "$deleted_files"
if [[ $restored_count -gt 0 ]]; then
echo "Safeguard: restored $restored_count non-framework files that would have been deleted by upstream merge"
fi
fi
# --- Part 2: Identity file content-replacement protection (v1.40.0) ---
# Project identity files describe YOUR project, not the framework.
# Merge conflict resolution (especially --theirs) can silently replace
# downstream content with upstream's version. This is invisible to
# --diff-filter=D because the file is modified, not deleted.
#
# Fixes: #439 — BUTTERFREEZONE.md overwritten during /update-loa
identity_files="README.md CHANGELOG.md BUTTERFREEZONE.md"
identity_restored=0
for identity_file in $identity_files; do
# Only check if the file existed before the merge AND was modified by it
if git show "HEAD:$identity_file" >/dev/null 2>&1 && \
git diff --cached --diff-filter=M --name-only | grep -qxF "$identity_file"; then
git checkout HEAD -- "$identity_file" 2>/dev/null && identity_restored=$((identity_restored + 1)) || true
fi
done
if [[ $identity_restored -gt 0 ]]; then
echo "Safeguard: restored $identity_restored project identity files that would have been overwritten by upstream merge"
fi
Why deletions? When upstream performs cleanup (removing template/example files),
git mergepropagates those deletions to downstream projects that share git history. This safeguard uses an allowlist of framework-managed paths — only deletions within the framework zone are permitted. All other files are restored from HEAD (pre-merge state), preserving downstream application code, configurations, and documentation.Why content replacements? Project identity files (README.md, CHANGELOG.md, BUTTERFREEZONE.md) are generated per-project and describe YOUR project. When merge conflicts on these files are resolved with
--theirs, the downstream project's identity is silently replaced with upstream's. The--diff-filter=Dcheck cannot catch this because the file is modified, not deleted..gitattributes merge=oursis the primary defense; this check is defense-in-depth.Fixes: #331 — cycle-014 merge deleting 933 downstream project files. #439 — BUTTERFREEZONE.md content overwrite.
Phase 5.5: Revert Protected Paths
Check for and revert any changes to protected paths that should not propagate to downstream projects. Since the merge is not yet committed (--no-commit), use git diff --cached and restore from HEAD:
# Check if .github/workflows/ has staged changes from the merge
workflow_changes=$(git diff --cached --name-only -- '.github/workflows/')
if [[ -n "$workflow_changes" ]]; then
while IFS= read -r f; do
if git show "HEAD:$f" >/dev/null 2>&1; then
# File existed before merge — restore pre-merge version
git checkout HEAD -- "$f"
else
# New file from upstream — unstage and remove
git rm -f --cached "$f" 2>/dev/null || true
rm -f "$f" 2>/dev/null || true
fi
done <<< "$workflow_changes"
fi
Why? GitHub requires the
workflowOAuth scope to push changes to.github/workflows/. Most downstream users don't have this scope. The.gitattributesmerge=oursrule protects existing workflow files, but new workflow files added upstream still propagate via merge. This step catches both cases. (Defense-in-depth: Phase 5.3 already handles workflow file deletions, but this phase additionally catches new and modified workflow files.)
Phase 5.7: Commit the Safeguarded Merge
After all safeguards have run, create the merge commit:
git commit -m "chore: update Loa framework"
Phase 5.8: Sync Constructs
After the merge commit, sync construct pack skills to ensure newly added skills in pack updates are registered:
if [[ -x ".claude/scripts/sync-constructs.sh" ]]; then
echo "Syncing construct packs..."
.claude/scripts/sync-constructs.sh
fi
Phase 6: Handle Merge Result
- Success: Show changelog excerpt and next steps
- Conflicts: List conflicted files with resolution guidance
Arguments
| Argument | Description | Required |
|---|---|---|
branch | Branch name to update from (default: main) | No |
Outputs
| Path | Description |
|---|---|
| Git merge commit | Merged upstream changes |
Merge Strategy
| File Location | Merge Behavior |
|---|---|
.claude/skills/ | Updated to latest Loa versions |
.claude/commands/ | Updated to latest Loa versions |
.claude/protocols/ | Updated to latest Loa versions |
.claude/scripts/ | Updated to latest Loa versions |
CLAUDE.md | Standard merge (may conflict) |
PROCESS.md | Standard merge (may conflict) |
app/ | Auto-preserved via Phase 5.3 collateral deletion safeguard |
grimoires/loa/prd.md | Auto-preserved via Phase 5.3 collateral deletion safeguard |
grimoires/loa/sdd.md | Auto-preserved via Phase 5.3 collateral deletion safeguard |
grimoires/loa/analytics/ | Auto-preserved via Phase 5.3 collateral deletion safeguard |
| All non-framework files | Auto-preserved via Phase 5.3 collateral deletion safeguard |
.github/workflows/ | Auto-preserved via .gitattributes + Phase 5.5 revert |
CHANGELOG.md | Auto-preserved via .gitattributes (merge=ours) |
README.md | Auto-preserved via .gitattributes (merge=ours) |
Note: All non-framework files are protected by the Phase 5.3 collateral deletion safeguard (v1.3.0). README.md, CHANGELOG.md, and
.github/workflows/files have additional protection via.gitattributesmerge=ours and Phase 5.5 revert. The pre-flight check ensures themerge.ours.driveris configured.
Conflict Resolution
Framework Files (.claude/)
Recommend accepting upstream version:
git checkout --theirs {filename}
Project Identity Files (CHANGELOG.md, README.md)
These files define YOUR project, not the Loa framework. ALWAYS keep your version:
git checkout --ours CHANGELOG.md README.md
Never accept upstream versions of these files - they contain Loa's template content, not your project's history and documentation.
Project Files
Manual resolution required:
- Open file and find conflict markers (
<<<<<<< HEAD) - Keep changes you want from both versions
- Remove conflict markers
- Save the file
After Resolving
git add .
git commit -m "chore: update Loa framework (conflicts resolved)"
Error Handling
| Error | Cause | Resolution |
|---|---|---|
| "Uncommitted changes" | Dirty working tree | Commit or stash changes first |
| "Remote not configured" | Missing loa/upstream remote | Add remote with git remote add |
| "Fetch failed" | Network or auth error | Check connection and remote URL |
| "Already up to date" | No new commits | Nothing to update |
| "Branch not found" | Remote branch doesn't exist | Check available branches with git branch -r | grep loa/ |
| "Invalid branch name" | Branch contains invalid characters | Only use alphanumeric, dash, underscore, slash, dot |
| "State file corrupt" | Invalid JSON in branch-testing.json | State auto-cleared, continue normally |
| "Safeguard: restored N files" | Upstream cleanup deleted non-framework files | Normal — safeguard working as intended |
Branch Testing Errors
Branch not found on remote:
Error: Branch 'feature/does-not-exist' not found on remote 'loa'
Available branches:
loa/main
loa/feature/constructs-multiselect
loa/fix/label-handling
To list all remote branches: git branch -r | grep loa/
Dirty working tree (with stash suggestion):
Error: Your working tree has uncommitted changes.
Quick fix: git stash push -m "before testing loa branch"
After testing: git stash pop
Next Steps After Update
- Review Loa releases for new features and changes
- Check
CLAUDE.mdfor new commands or workflow updates
technical
- github
- 0xHoneyJar/loa-freeside
- stars
- 7
- license
- NOASSERTION
- contributors
- 6
- last commit
- 2026-04-30T00:44:24Z
- file
- .claude/commands/update-loa.md