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:
- Development work is pushed to GitHub
- Protected installation still runs old code
- All projects using the protected installation via symlinks see stale code
- 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
- Automatic - Sync should happen without manual intervention after push
- Atomic - Protected installation should be updated atomically (swap, not incremental)
- Preserve Local Data - Machine-specific files (machine-id.json, session-logs/, context-storage/) must be preserved
- Non-Blocking - Push should not fail if sync fails (graceful degradation)
- Reversible - Ability to rollback if sync corrupts protected installation
Decision
Implement a git push wrapper with automatic post-push sync that:
- Executes normal
git push - If push succeeds, automatically sync to protected installation
- Uses atomic swap pattern (clone → prepare → swap → cleanup)
- Preserves machine-specific data
- 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.
| Component | Path | Purpose |
|---|---|---|
| Push wrapper | scripts/git-push-sync.py | Main entry point, orchestrates push + sync |
| Post-push hook | hooks/git/post-push-sync.sh | Shell hook for manual/alias use |
| Git alias | ~/.gitconfig | git pushsync alias |
| Command | commands/git-sync.md | /git-sync skill documentation |
| Session log | session-logs/SESSION-LOG-*.md | Updated 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 Point | Behavior | Recovery |
|---|---|---|
| Git push fails | Exit immediately, no sync | Fix git issue, retry |
| Clone fails | Push succeeded, warn user | Manual /framework-sync --update-only |
| Swap fails | Rollback to backup | Automatic, protected unchanged |
| Verify fails | Warn but continue | Manual 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
- Automatic - No manual step to remember after push
- Consistent - Protected installation always matches latest push
- Safe - Atomic swap prevents partial updates
- Preserves Data - Machine-specific files never lost
- Transparent - Clear feedback on sync status
- Graceful Degradation - Push still works if sync fails
Negative
- Slower Push - Additional clone + swap adds ~10-15 seconds
- Network Dependency - Sync requires internet (can't sync from local only)
- Shallow Clone - No git history in protected (by design, but limits debugging)
Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Concurrent syncs | Low | Corruption | File-based lock |
| Network interruption mid-sync | Medium | Stale protected | Rollback mechanism |
| Disk full during swap | Low | Failed sync | Check disk space first |
| Permission issues | Low | Failed sync | Validate 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
| Term | Definition |
|---|---|
| Protected Installation | Read-only CODITECT framework at ~/Library/Application Support/CODITECT/core/ |
| Atomic Swap | Update pattern where old installation is replaced in single move operation |
| Shallow Clone | Git clone with --depth 1 containing only latest commit (faster) |
| Machine-Specific Files | Files unique to each developer machine (machine-id.json, session-logs/, context-storage/) |
| Push Wrapper | Script that executes git push and additional post-push actions |
| Post-Push Hook | Script triggered after successful git push |
| Framework-Sync | Existing manual 7-step sync process (ADR-106) |
| Rollback | Automatic restoration of previous state on failure |
References
- ADR-057: CODITECT Core Initial Setup
- ADR-058: Machine-Specific Session Logs
- ADR-106: Contributor Sync
- framework-sync.py
ADR-113 | Created: 2026-01-25 | Status: Proposed