| name | murmurent-project-push |
| description | Back up a WHOLE murmurent project in one go — commit + push every repo the project owns (code, docs, manuscript, …) that lives on this machine. Runs the same per-repo safety pre-flight as /murmurent-push (secret scan, secret-shaped filenames, governed-data guard, large-file guard), blocks any repo that fails without touching the rest, and posts one plain-language summary + one Slack note to the project's own channel. Use when someone says "back up my project", "save everything", or "push all the project's repos". |
| user_invocable | true |
Commit and push every repo a project owns in a single command — not just the
one repo you happen to be standing in. A murmurent project (a cert-project) can
carry several repos: a code repo, a docs/manuscript repo, a data-pointer repo,
and so on. This skill loops over that authoritative repo list and safely backs up
each one that is cloned on this computer.
For a single repo, use /murmurent-push. This skill is the project-wide
sibling: it reuses /murmurent-push's pre-flight per repo, then rolls the results
into one novice-friendly summary.
When to use it
- You are anywhere inside a project repo (or you know the project name) and want
all of the project's repos backed up to GitHub at once.
- Someone asks to "back up the project", "save my work", or "push everything"
and the work spans more than one repo.
If the request is clearly about just the current repo, prefer /murmurent-push.
What it runs
One command does the whole job:
murmurent project push
murmurent project push --project <name>
murmurent project push -m "sync before the review"
murmurent project push --detail
The command resolves the project (from your current directory, or --project),
enumerates its repos, and for each repo that is cloned on this machine:
- runs the pre-flight (below);
- if it passes and there are changes: stages selectively, commits (your
-m message, else an autogenerated project push: N files updated), and
pushes to the repo's tracking remote;
- records a per-repo result.
Then it prints one summary line + one line per repo, and posts a single Slack
note to the project's own channel (skipped silently if the project has no
channel yet — that is a supported state, not an error).
Exit codes: 0 everything clean/pushed · 1 partial (one or more repos need
attention) · 2 nothing on this machine to push (or the project wasn't found).
Pre-flight (inherited per repo, before that repo is committed)
Each repo is checked independently and before its own git add, so a
problem in one repo never contaminates a sibling. A repo that fails any gate is
blocked: no commit, no push, nothing left staged.
| Gate | What it catches | On a hit |
|---|
| Governed-data paths | a change under immutable/, append_only/ (or legacy raw//refined/) | block the repo — that data must never go to GitHub |
| Secret-shaped filenames | *.env, *.pem/*.key/*.p12, id_rsa/id_ed25519, *credentials*, .netrc, … | block the repo — remove it or keep it outside the repo |
| Staged-content secret scan | a hardcoded AWS/GitHub/Slack/Google token or private-key block inside a file | block the repo — findings are shown redacted, never the raw secret |
| Large files | any file > 1 MB under data//src//exp//obsolete/ | block the repo — move it to the project's data area |
.claude/settings.json | machine-absolute paths / per-machine allowlists | never staged; added to .gitignore instead (the rest of the repo still pushes) |
The secret-content gate is exactly core.secret_scan.scan_staged — the same
scanner /murmurent-push and murmurent security secrets-scan --staged use — run
against the staged blobs (what a push would actually publish).
Repos that aren't cloned here, or that live on another host, are skipped with a
message, not treated as errors. A push the remote rejects (no write access)
leaves the commit local and is reported per repo — the other repos still push.
Happy-path output
> /murmurent-project-push
✔ 3 of 3 repos safely backed up to GitHub.
✔ brca_code — saved to GitHub.
✔ brca_docs — saved to GitHub.
✔ brca_manuscript — nothing new to save.
Output when something needs attention
> /murmurent-project-push
⚠ 2 of 5 repos safely backed up to GitHub. 1 needs attention. 2 skipped.
✔ brca_code — saved to GitHub.
✔ brca_docs — saved to GitHub.
✗ brca_secrets — a secret was found in config.py (line 12) — remove it and load
it from an environment variable instead, then run this again.
· brca_hpc — lives on cluster; save it from there.
· brca_data — isn't on this computer, so it was skipped.
Every line says the exact next step in plain language — no git jargon. Use
--detail when a power user wants the branch + short hash + git-level reason.
Managed GitHub mirrors (multi-account backup)
A project's repo can be backed up to more than one GitHub account/org — the
inter-group case where each lab's org wants its own copy. Record the extra
destinations once; every murmurent project push then updates them in addition
to the primary origin, and reports each destination separately:
murmurent project mirror add code_repo labB/brca-code
murmurent project mirror add code_repo git@github.com:labC/brca-code.git
murmurent project mirror list code_repo
murmurent project mirror remove code_repo labB/brca-code
On the next push, each repo's branch goes to origin first, then to each mirror:
✔ brca_code — saved to GitHub.
✔ also backed up to labB: backed up
⚠ also backed up to labC: could not reach — tell your PI
How mirrors behave:
- Each mirror is an explicit, named git remote (
mm-mirror-<slug>) created and
kept in sync idempotently at push time — re-running never duplicates it.
origin is never touched, and no silent multiple-push-URLs are used, so
every destination's success/failure is reported on its own line.
- A mirror the pusher can't authenticate to (or that's unreachable) is a
per-remote failure — it never rolls back or blocks the primary push.
The primary still reaches
origin; the mirror line reads "could not reach —
tell your PI". A run with any mirror failure exits 1 (partial).
- Authentication is whatever your
git/gh already has for that org. An
org/name shorthand is expanded to a GitHub HTTPS URL (so your stored gh
credentials apply); pin a full git@… URL instead to force SSH.
What this skill does NOT do
- No inter-group Slack guest invites (Phase 3) and no server-access checks
(Phase 4).
- Never
--force, --amend, or --no-verify — the lab's history is the audit
trail and murmurent's hooks are load-bearing.
- Does not run tests, bump versions, or open PRs. Run tests yourself first.
- Does not touch the governed data root (
immutable//append_only/); the
pre-flight refuses any diff that would.
Refusals
| Situation | What happens |
|---|
Not inside a project repo and no --project given | stop; ask for --project <name> |
| Project name not found in the registry | exit 2; nothing pushed |
| A repo has a governed-data path / secret / secret-shaped file / oversize file | that repo is blocked (exit 1); siblings still push |
| A repo isn't cloned here / lives on another host | skipped with a message (not an error) |
| A repo's push is rejected (no write access) | commit stays local; reported per repo (exit 1) |
| No Slack channel for the project | the push still succeeds; the Slack note is skipped silently |