Repo Sync
Smart pull-all with automatic diagnosis and repair for the workspace-hub multi-repo ecosystem.
Usage
/repo-sync — Pull all repos, diagnose failures, and fix them.
Optional arguments:
/repo-sync pull— Pull all repos (default)/repo-sync status— Status check only, no pulls/repo-sync push— Push all repos with unpushed commits
What It Does
Phase 1: Bulk Pull
Run ./scripts/repository_sync pull all to attempt pulling every repo.
Phase 2: Diagnose Failures
For each repo that failed, check:
- Detached HEAD — submodules pinned at a commit, not on a branch
- Diverged branches — local and remote have diverged, needs merge
- Uncommitted changes — dirty working tree blocking pull
- No upstream — no tracking branch configured
Phase 3: Auto-Fix
Apply the appropriate fix per failure type:
| Failure | Fix |
|---|---|
| Detached HEAD (submodule) | git checkout main && git pull --no-rebase |
| Diverged branches | git pull --no-rebase (merge strategy) |
| Uncommitted changes | git stash && git pull --no-rebase && git stash pop |
| No upstream | Report only, no auto-fix |
| Large repo partial checkout / timed-out pull | Use no-untracked status probes, kill hung sync/status processes if needed, preserve recovery artifacts under .git/recovery-backups/, restore accidental tracked deletions; see references/large-repo-partial-checkout-recovery.md |
| Archived/read-only remote | Do not force workaround; preserve/export local-only commits and open a follow-up issue |
Archived / Read-only Remote Exception Handling
If a repo is ahead locally but push fails because the remote is archived or read-only:
- Confirm the state explicitly:
- current branch
- local HEAD vs upstream HEAD
- ahead commit list (
git log @{u}..HEAD) - diffstat for the ahead range
- Do NOT try to bypass the archive/read-only state.
- If the ahead range is non-trivial, export a preservation artifact:
git format-patch -1 <commit> --stdout > <workspace-hub-artifact>.patch
- Write a short decision memo with explicit options:
- abandon
- archive-only
- selective port to a writable successor
- Open a GitHub issue documenting the exception and link the exported patch artifact.
- Default recommendation when there is no clear writable successor and the diff is large/destructive:
archive-only.
This came up with pyproject-starter, where the remote was archived/read-only and the only ahead commit was a massive destructive sync commit. The safe action was to preserve the patch in repo artifacts and require explicit human review before any migration.
Phase 4: Encoding Health Check
After pulling, run the encoding check against all work queue and skill files to surface any Windows-created UTF-16 / CRLF files that came in via the pull:
.Codex/hooks/check-encoding.sh
This runs in warn-only mode (post-merge behaviour) — it reports bad files but does not block. Any files flagged should be converted and committed immediately:
# Convert a UTF-16 file to UTF-8
iconv -f UTF-16 -t UTF-8 <file> | sed 's/\r//' > /tmp/fixed.md
mv /tmp/fixed.md <file>
git add <file> && git commit -m "fix(encoding): convert <file> to UTF-8"
Phase 5: Ecosystem Health + Summary
After the encoding check, spawn an ecosystem health agent in the background:
Task(
subagent_type="Bash",
description="Ecosystem health check",
prompt="Run the 14-check suite from /ecosystem-health skill. Report pass/fail/warn for each group.",
run_in_background=True
)
This does not block the pull summary. Report health results alongside repo status.
See /ecosystem-health skill for the full check suite and pass conditions.
Implementation
When this skill is invoked, execute these steps:
Step 1: Run bulk pull
./scripts/repository_sync pull all
Capture output. Identify repos marked with ✗ Failed.
Step 2: For each failed repo, diagnose
cd <repo_path>
# Check if on a branch
git branch --show-current # empty = detached HEAD
# Check for uncommitted changes
git status --porcelain
# Check divergence (only if on a branch with upstream)
git rev-list --count @{u}.. 2>/dev/null # ahead
git rev-list --count ..@{u} 2>/dev/null # behind
Step 3: Apply fixes
Run all independent repo fixes in parallel using the Bash tool.
Detached HEAD:
cd <repo_path> && git checkout main && git pull --no-rebase
Diverged branches:
cd <repo_path> && git pull --no-rebase
Uncommitted changes blocking pull:
cd <repo_path> && git stash && git pull --no-rebase && git stash pop
If stash pop has conflicts, report to user — do NOT auto-resolve.
Step 4: Report summary table
Format as markdown table:
| Repo | Issue | Fix Applied | Result |
|------|-------|-------------|--------|
Important Notes
- Never force-push or
reset --hardwithout explicit user approval - Never rebase diverged branches — always merge (per workspace AGENTS.md)
- digitalmodel and worldenergydata are submodules — detached HEAD is normal when workspace-hub pins a specific commit
- After fixing submodules, the workspace-hub
git statuswill show them as modified (new submodule pointer) — this is expected - If
stash popfails with conflicts, stop and report to user - Use
--no-rebaseon all pulls to avoid rebase surprises on diverged repos - Encoding check runs after every pull — UTF-16 files from Windows editors
crash
generate-index.pyand other parsers silently. Fix immediately on detection. - If the user explicitly wants all dirty/untracked files tracked, run a second pass that stages with
git add -A, commits on the repo's current branch, and pushes that branch before attempting broader branch-merging work. - For branch-merging across many repos, merge into each repo's actual default branch (
mainormaster) detected from GitHub /origin/HEAD; do not assumemain. - For large batch merges, use a temporary worktree checked out from the default branch (prefer
origin/<default>if available) so merges are isolated from the user's current working tree and local dirty state. - Workspace-hub can mutate state during commit/push hooks (
.Codex/state/*, logs, generated reports). After a commit or failed push, always re-rungit statusand re-fetch before retrying; apparent ref-lock push failures may be stale-expectation races rather than true divergence. - New-branch pushes in workspace-hub may trigger expensive pre-push checks across tier-1 repos and can time out. If the user has approved sensible commands and the goal is repo hygiene/sync rather than validation,
git push --no-verifymay be necessary after verifying local/remote state. - Archived/read-only repos can still be committed locally for preservation, but push/merge to remote will fail; report them explicitly as blocked rather than retrying.
- In huge repos, do not run broad
git status --porcelain -uall, fulldu, or full checkout/pull loops as the first recovery move. Start with no-untracked status and ahead/behind probes, then followreferences/large-repo-partial-checkout-recovery.md. - If interrupted sync creates
.Codex.partial-pull-backup-*,.codex.partial-pull-backup-*, or generated provider cache trees, preserve them inside.git/recovery-backups/instead of committing or deleting them; they are recovery artifacts unless the user explicitly says otherwise.
Iron Law
No destructive git operation (force-push, reset --hard, auto-resolve conflicts) shall be performed without explicit user confirmation — ever.
Rationalization Defense
| Excuse | Reality |
|---|---|
| "The conflict is trivial — I can auto-resolve it" | Trivial-looking conflicts have destroyed production data. Stash pop conflicts go to the user, always. |
| "Force-push is safe here because nobody else uses this branch" | You do not have full visibility into all machines and sessions using this repo. Assume others are active. |
| "I need to reset --hard to get to a clean state" | Use git stash to preserve work, then pull. Hard reset is data destruction, not cleanup. |
| "The encoding check is slow, I'll skip it this time" | UTF-16 files crash parsers silently. Skipping the check means shipping a time bomb. |
Red Flags
These phrases signal you are about to violate the Iron Law:
- "let me just force-push to fix this"
- "I'll reset to a clean state"
- "this merge conflict is simple enough to auto-resolve"
- "skipping the encoding check to save time"
- "nobody else is working on this repo"