Skip to main content

ADR-113: Post-Push Protected Installation Sync

Status

Proposed

Context

Problem Statement

Contributors to CODITECT work in the submodule at coditect-rollout-master/submodules/core/coditect-core, push changes to GitHub, but the protected installation at ~/Library/Application Support/CODITECT/core/ remains stale until manually running /framework-sync --update-only.

This creates a gap where:

  1. Development work is pushed to GitHub
  2. Protected installation still runs old code
  3. All projects using the protected installation via symlinks see stale code
  4. Contributors must remember to manually sync

Current Architecture

SUBMODULE (git-tracked)          GITHUB                    PROTECTED (read-only)
coditect-rollout-master/ coditect-ai/ ~/Library/.../CODITECT/core/
submodules/core/coditect-core coditect-core
│ │ │
├── git push ───────────────┤ │
│ │ │
│ ❌ GAP: No auto-sync ──────────────────────────►│
│ │ │
└── /framework-sync ────────┴──────────────────────────────┤
(MANUAL - easy to forget) │

Requirements

  1. Automatic - Sync should happen without manual intervention after push
  2. Atomic - Protected installation should be updated atomically (swap, not incremental)
  3. Preserve Local Data - Machine-specific files (machine-id.json, session-logs/, context-storage/) must be preserved
  4. Non-Blocking - Push should not fail if sync fails (graceful degradation)
  5. Reversible - Ability to rollback if sync corrupts protected installation

Decision

Implement a git push wrapper with automatic post-push sync that:

  1. Executes normal git push
  2. If push succeeds, automatically sync to protected installation
  3. Uses atomic swap pattern (clone → prepare → swap → cleanup)
  4. Preserves machine-specific data
  5. Provides clear feedback on sync status

1. Architecture

CONTRIBUTOR PUSHES CODE


┌─────────────────────────────────────────────────────────────────┐
│ git-push-sync (wrapper script) │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Step 1: Execute git push │ │
│ │ └── git push origin main │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ (if push succeeds) │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Step 2: Update session log │ │
│ │ └── Append sync entry to SESSION-LOG-YYYY-MM-DD.md │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Step 3: Atomic sync to protected │ │
│ │ ├── Clone fresh from GitHub (shallow) │ │
│ │ ├── Remove .git directory │ │
│ │ ├── Remove recursive .claude/.coditect symlinks │ │
│ │ ├── Preserve machine-specific files │ │
│ │ ├── Atomic swap (old → backup, new → protected) │ │
│ │ └── Cleanup backup │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Step 4: Update parent submodule reference │ │
│ │ └── cd ../.. && git add submodules/core/... │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘


PROTECTED INSTALLATION UPDATED
(all symlinked projects now see new code)

2. Implementation Components

Note: This workflow is for CODITECT contributors only. Customers do not update coditect-core directly - they receive updates via the protected installation sync mechanism.

ComponentPathPurpose
Push wrapperscripts/git-push-sync.pyMain entry point, orchestrates push + sync
Post-push hookhooks/git/post-push-sync.shShell hook for manual/alias use
Git alias~/.gitconfiggit pushsync alias
Commandcommands/git-sync.md/git-sync skill documentation
Session logsession-logs/SESSION-LOG-*.mdUpdated on each push with sync status

3. Atomic Sync Protocol

atomic_sync_protocol:
step_1_prepare:
action: "Clone fresh from GitHub"
command: "git clone --depth 1 https://github.com/coditect-ai/coditect-core.git /tmp/coditect-sync/core_NEW"
rationale: "Shallow clone for speed, full fresh copy for integrity"

step_2_sanitize:
actions:
- "Remove .git directory"
- "Remove .claude and .coditect symlinks (prevent recursion)"
- "Set read-only permissions (0o444 files, 0o555 scripts)"

step_3_preserve:
files_to_copy:
- source: "PROTECTED/machine-id.json"
dest: "NEW/machine-id.json"
- source: "PROTECTED/session-logs/"
dest: "NEW/session-logs/"
- source: "PROTECTED/context-storage/"
dest: "NEW/context-storage/"

step_4_swap:
sequence:
- "mv PROTECTED → BACKUP"
- "mv NEW → PROTECTED"
- "rm -rf BACKUP"
rollback_on_failure: true

step_5_verify:
checks:
- "Component counts match (agents, skills, commands)"
- "Symlinks still valid"
- "No .git in protected"

4. Usage

Primary (recommended):

# From coditect-core submodule directory
python3 scripts/git-push-sync.py

# Or with message
python3 scripts/git-push-sync.py -m "feat: Add new feature"

Via git alias:

# Add to ~/.gitconfig
[alias]
pushsync = "!python3 ~/.coditect/scripts/git-push-sync.py"

# Usage
git pushsync
git pushsync -m "feat: Add new feature"

Via skill:

/git-sync           # Interactive mode
/git-sync --push # Push + sync
/git-sync --yes # Skip confirmations

5. Failure Handling

Failure PointBehaviorRecovery
Git push failsExit immediately, no syncFix git issue, retry
Clone failsPush succeeded, warn userManual /framework-sync --update-only
Swap failsRollback to backupAutomatic, protected unchanged
Verify failsWarn but continueManual investigation

6. Configuration

// ~/.coditect/config/sync-config.json
{
"post_push_sync": {
"enabled": true,
"auto_parent_update": true,
"verify_after_sync": true,
"shallow_clone": true,
"preserve_paths": [
"machine-id.json",
"session-logs/",
"context-storage/"
]
}
}

Consequences

Positive

  1. Automatic - No manual step to remember after push
  2. Consistent - Protected installation always matches latest push
  3. Safe - Atomic swap prevents partial updates
  4. Preserves Data - Machine-specific files never lost
  5. Transparent - Clear feedback on sync status
  6. Graceful Degradation - Push still works if sync fails

Negative

  1. Slower Push - Additional clone + swap adds ~10-15 seconds
  2. Network Dependency - Sync requires internet (can't sync from local only)
  3. Shallow Clone - No git history in protected (by design, but limits debugging)

Risks

RiskLikelihoodImpactMitigation
Concurrent syncsLowCorruptionFile-based lock
Network interruption mid-syncMediumStale protectedRollback mechanism
Disk full during swapLowFailed syncCheck disk space first
Permission issuesLowFailed syncValidate permissions

Implementation

Phase 1: Core Script (This Session)

  • Create hooks/git/post-push-sync.sh
  • Create scripts/git-push-sync.py
  • Create commands/git-sync.md
  • Update config/sync-config.json

Phase 2: Integration (Next Session)

  • Add git alias to CODITECT-CORE-INITIAL-SETUP.py
  • Update framework-sync.py to use shared atomic sync logic
  • Add tests

Phase 3: Documentation

  • Update CLAUDE.md with new workflow
  • Update contributor guide

Glossary

TermDefinition
Protected InstallationRead-only CODITECT framework at ~/Library/Application Support/CODITECT/core/
Atomic SwapUpdate pattern where old installation is replaced in single move operation
Shallow CloneGit clone with --depth 1 containing only latest commit (faster)
Machine-Specific FilesFiles unique to each developer machine (machine-id.json, session-logs/, context-storage/)
Push WrapperScript that executes git push and additional post-push actions
Post-Push HookScript triggered after successful git push
Framework-SyncExisting manual 7-step sync process (ADR-106)
RollbackAutomatic restoration of previous state on failure

References


ADR-113 | Created: 2026-01-25 | Status: Proposed