Skill Index

loa-freeside/

update-loa

community[command]

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-freeside

details

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:

  1. Checkout for testing (Recommended) - Creates a local test/loa-{branch} branch from the remote
  2. 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)
  • loa or upstream remote must be configured
  • Merge driver configured (one-time): git config merge.ours.driver true

Workflow

Phase 1: Pre-flight Checks

  1. Verify working tree is clean
  2. 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-commit flag 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-commit exits 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:

  1. Deletions (--diff-filter=D): Files deleted by upstream cleanup that should not propagate
  2. 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 merge propagates 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=D check cannot catch this because the file is modified, not deleted. .gitattributes merge=ours is 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 workflow OAuth scope to push changes to .github/workflows/. Most downstream users don't have this scope. The .gitattributes merge=ours rule 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

ArgumentDescriptionRequired
branchBranch name to update from (default: main)No

Outputs

PathDescription
Git merge commitMerged upstream changes

Merge Strategy

File LocationMerge 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.mdStandard merge (may conflict)
PROCESS.mdStandard merge (may conflict)
app/Auto-preserved via Phase 5.3 collateral deletion safeguard
grimoires/loa/prd.mdAuto-preserved via Phase 5.3 collateral deletion safeguard
grimoires/loa/sdd.mdAuto-preserved via Phase 5.3 collateral deletion safeguard
grimoires/loa/analytics/Auto-preserved via Phase 5.3 collateral deletion safeguard
All non-framework filesAuto-preserved via Phase 5.3 collateral deletion safeguard
.github/workflows/Auto-preserved via .gitattributes + Phase 5.5 revert
CHANGELOG.mdAuto-preserved via .gitattributes (merge=ours)
README.mdAuto-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 .gitattributes merge=ours and Phase 5.5 revert. The pre-flight check ensures the merge.ours.driver is 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:

  1. Open file and find conflict markers (<<<<<<< HEAD)
  2. Keep changes you want from both versions
  3. Remove conflict markers
  4. Save the file

After Resolving

git add .
git commit -m "chore: update Loa framework (conflicts resolved)"

Error Handling

ErrorCauseResolution
"Uncommitted changes"Dirty working treeCommit or stash changes first
"Remote not configured"Missing loa/upstream remoteAdd remote with git remote add
"Fetch failed"Network or auth errorCheck connection and remote URL
"Already up to date"No new commitsNothing to update
"Branch not found"Remote branch doesn't existCheck available branches with git branch -r | grep loa/
"Invalid branch name"Branch contains invalid charactersOnly use alphanumeric, dash, underscore, slash, dot
"State file corrupt"Invalid JSON in branch-testing.jsonState auto-cleared, continue normally
"Safeguard: restored N files"Upstream cleanup deleted non-framework filesNormal — 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.md for 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

related