Skip to content

Commit 401ccda

Browse files
NagyViktNagyVikt
andauthored
Publish the current musafety workflow changes from main (#11)
This snapshot publishes the pending guardrail workflow updates that were already prepared in the main working tree, including sandbox helper scripts, codex-agent setup wiring, and repository-local safety assets. It also captures the package/version bump and ignore-rule update used to keep local oh-my-codex clones out of status noise. Constraint: User explicitly asked to publish the current main changes Constraint: Protected-branch hooks require explicit bypass for non-VS Code terminal commits Rejected: Re-route through a feature branch PR | user asked for direct main publish Confidence: medium Scope-risk: moderate Directive: Keep template scripts and repo-local installed script copies synchronized in future edits Tested: npm test (37/37 pass); node --check bin/multiagent-safety.js Not-tested: GitHub remote protected-branch policy outcome until push attempt Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
1 parent 8b511fa commit 401ccda

17 files changed

Lines changed: 1513 additions & 4 deletions

.claude/commands/musafety.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# /musafety
2+
3+
Run a musafety check-and-repair workflow for the current repository.
4+
5+
## Steps
6+
7+
1. Run `musafety status`.
8+
2. If status is degraded, run `musafety doctor`.
9+
3. If still degraded, run `musafety scan` and summarize each finding with a fix.
10+
4. Report final verdict as one of:
11+
- `Repo is musafe`
12+
- `Repo is not musafe` (include blockers)
13+
14+
## Style
15+
16+
- Keep output short and operational.
17+
- Include exact commands you executed.
18+
- Prefer concrete next actions over generic advice.

.codex/skills/musafety/SKILL.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
name: musafety
3+
description: "Use when you need to check, repair, or bootstrap multi-agent safety guardrails in this repository."
4+
---
5+
6+
# musafety (Codex skill)
7+
8+
Use this skill whenever branch safety, lock ownership, or guardrail setup may be broken.
9+
10+
## Fast path
11+
12+
1. Run `musafety status`.
13+
2. If repo safety is degraded, run `musafety doctor`.
14+
3. If issues remain, run `musafety scan` and address the findings.
15+
16+
## Setup path
17+
18+
If guardrails are missing entirely, run:
19+
20+
```sh
21+
musafety setup
22+
```
23+
24+
Then verify:
25+
26+
```sh
27+
musafety status
28+
musafety scan
29+
```
30+
31+
## Operator notes
32+
33+
- Prefer `musafety doctor` for one-step repair + verification.
34+
- Keep agent work isolated (`agent/*` branches + lock claims).
35+
- Do not bypass protected branch safeguards unless explicitly required.

.githooks/pre-commit

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
5+
if [[ -z "$branch" ]]; then
6+
exit 0
7+
fi
8+
9+
if [[ "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
10+
exit 0
11+
fi
12+
13+
is_vscode_git_context=0
14+
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" ]]; then
15+
is_vscode_git_context=1
16+
fi
17+
18+
allow_vscode_protected_branch_raw="${MUSAFETY_ALLOW_VSCODE_PROTECTED_BRANCH:-$(git config --get multiagent.protectedBranches.allowVSCode || true)}"
19+
allow_vscode_protected_branch_raw="$(printf '%s' "${allow_vscode_protected_branch_raw:-}" | tr '[:upper:]' '[:lower:]')"
20+
allow_vscode_protected_branch=0
21+
case "$allow_vscode_protected_branch_raw" in
22+
1|true|yes|on) allow_vscode_protected_branch=1 ;;
23+
*) allow_vscode_protected_branch=0 ;;
24+
esac
25+
26+
protected_branches_raw="${MUSAFETY_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
27+
if [[ -z "$protected_branches_raw" ]]; then
28+
protected_branches_raw="dev main master"
29+
fi
30+
protected_branches_raw="${protected_branches_raw//,/ }"
31+
32+
is_protected_branch=0
33+
for protected_branch in $protected_branches_raw; do
34+
if [[ "$branch" == "$protected_branch" ]]; then
35+
is_protected_branch=1
36+
break
37+
fi
38+
done
39+
40+
if [[ "$is_protected_branch" == "1" ]]; then
41+
if [[ "$is_vscode_git_context" == "1" && "$allow_vscode_protected_branch" == "1" ]]; then
42+
exit 0
43+
fi
44+
45+
git_dir="$(git rev-parse --git-dir)"
46+
if [[ -f "$git_dir/MERGE_HEAD" ]]; then
47+
exit 0
48+
fi
49+
50+
cat >&2 <<'MSG'
51+
[agent-branch-guard] Direct commits on protected branches are blocked.
52+
Use an agent branch first:
53+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
54+
After finishing work:
55+
bash scripts/agent-branch-finish.sh
56+
57+
Optional repo override to allow VS Code Source Control commits:
58+
git config multiagent.protectedBranches.allowVSCode true
59+
60+
Temporary bypass (not recommended):
61+
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
62+
MSG
63+
exit 1
64+
fi
65+
66+
if [[ "$branch" == agent/* ]]; then
67+
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
68+
cat >&2 <<'MSG'
69+
[agent-branch-guard] Agent branch commits require file ownership locks.
70+
Claim files first:
71+
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
72+
MSG
73+
exit 1
74+
fi
75+
76+
require_sync_before_commit_raw="$(git config --get multiagent.sync.requireBeforeCommit || true)"
77+
if [[ -z "$require_sync_before_commit_raw" ]]; then
78+
require_sync_before_commit_raw="false"
79+
fi
80+
require_sync_before_commit="$(printf '%s' "$require_sync_before_commit_raw" | tr '[:upper:]' '[:lower:]')"
81+
82+
should_require_sync=0
83+
case "$require_sync_before_commit" in
84+
1|true|yes|on) should_require_sync=1 ;;
85+
0|false|no|off) should_require_sync=0 ;;
86+
*) should_require_sync=0 ;;
87+
esac
88+
89+
if [[ "$should_require_sync" == "1" ]]; then
90+
base_branch="$(git config --get multiagent.baseBranch || true)"
91+
if [[ -z "$base_branch" ]]; then
92+
base_branch="dev"
93+
fi
94+
95+
max_behind_raw="$(git config --get multiagent.sync.maxBehindCommits || true)"
96+
if [[ -z "$max_behind_raw" ]]; then
97+
max_behind_raw="0"
98+
fi
99+
if [[ ! "$max_behind_raw" =~ ^[0-9]+$ ]]; then
100+
echo "[agent-sync-guard] Invalid multiagent.sync.maxBehindCommits value: ${max_behind_raw}" >&2
101+
echo "[agent-sync-guard] Expected non-negative integer. Example: git config multiagent.sync.maxBehindCommits 0" >&2
102+
exit 1
103+
fi
104+
105+
if ! git fetch origin "$base_branch" --quiet >/dev/null 2>&1; then
106+
echo "[agent-sync-guard] Unable to fetch origin/${base_branch} while commit sync gate is enabled." >&2
107+
echo "[agent-sync-guard] Disable gate temporarily with: git config multiagent.sync.requireBeforeCommit false" >&2
108+
exit 1
109+
fi
110+
111+
if ! git show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
112+
echo "[agent-sync-guard] Remote base branch not found: origin/${base_branch}" >&2
113+
exit 1
114+
fi
115+
116+
behind_count="$(git rev-list --left-right --count "${branch}...origin/${base_branch}" 2>/dev/null | awk '{print $2}')"
117+
behind_count="${behind_count:-0}"
118+
max_behind="${max_behind_raw}"
119+
120+
if [[ "$behind_count" -gt "$max_behind" ]]; then
121+
cat >&2 <<MSG
122+
[agent-sync-guard] Commit blocked: '${branch}' is behind origin/${base_branch} by ${behind_count} commit(s) (max allowed: ${max_behind}).
123+
Run:
124+
musafety sync --base ${base_branch}
125+
Or relax threshold:
126+
git config multiagent.sync.maxBehindCommits <n>
127+
MSG
128+
exit 1
129+
fi
130+
fi
131+
fi
132+
133+
if command -v pre-commit >/dev/null 2>&1 && [[ -f .pre-commit-config.yaml ]]; then
134+
pre-commit run --hook-stage pre-commit
135+
fi

.githooks/pre-push

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ "${ALLOW_PUSH_ON_PROTECTED_BRANCH:-0}" == "1" || "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
5+
exit 0
6+
fi
7+
8+
is_vscode_git_context=0
9+
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" ]]; then
10+
is_vscode_git_context=1
11+
fi
12+
13+
allow_vscode_protected_branch_raw="${MUSAFETY_ALLOW_VSCODE_PROTECTED_BRANCH:-$(git config --get multiagent.protectedBranches.allowVSCode || true)}"
14+
allow_vscode_protected_branch_raw="$(printf '%s' "${allow_vscode_protected_branch_raw:-}" | tr '[:upper:]' '[:lower:]')"
15+
allow_vscode_protected_branch=0
16+
case "$allow_vscode_protected_branch_raw" in
17+
1|true|yes|on) allow_vscode_protected_branch=1 ;;
18+
*) allow_vscode_protected_branch=0 ;;
19+
esac
20+
21+
if [[ "$is_vscode_git_context" == "1" && "$allow_vscode_protected_branch" == "1" ]]; then
22+
exit 0
23+
fi
24+
25+
protected_branches_raw="${MUSAFETY_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
26+
if [[ -z "$protected_branches_raw" ]]; then
27+
protected_branches_raw="dev main master"
28+
fi
29+
protected_branches_raw="${protected_branches_raw//,/ }"
30+
31+
is_protected_branch() {
32+
local branch="$1"
33+
for protected_branch in $protected_branches_raw; do
34+
if [[ "$branch" == "$protected_branch" ]]; then
35+
return 0
36+
fi
37+
done
38+
return 1
39+
}
40+
41+
blocked_refs=()
42+
while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do
43+
if [[ -z "${remote_ref:-}" || "$remote_ref" != refs/heads/* ]]; then
44+
continue
45+
fi
46+
47+
remote_branch="${remote_ref#refs/heads/}"
48+
if is_protected_branch "$remote_branch"; then
49+
blocked_refs+=("$remote_branch")
50+
fi
51+
done
52+
53+
if [[ "${#blocked_refs[@]}" -gt 0 ]]; then
54+
{
55+
echo "[agent-branch-guard] Push to protected branch blocked."
56+
echo "[agent-branch-guard] Protected target(s): ${blocked_refs[*]}"
57+
echo "[agent-branch-guard] Push from an agent branch and merge via PR."
58+
echo "[agent-branch-guard] Optional repo override for VS Code Source Control:"
59+
echo " git config multiagent.protectedBranches.allowVSCode true"
60+
echo
61+
echo "Temporary bypass (not recommended):"
62+
echo " ALLOW_PUSH_ON_PROTECTED_BRANCH=1 git push ..."
63+
} >&2
64+
exit 1
65+
fi
66+
67+
exit 0

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.omx/
2-
node_modules
2+
node_modules
3+
oh-my-codex/

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Example output:
128128
npm i -g musafety
129129
musafety setup
130130
musafety doctor
131+
bash scripts/codex-agent.sh "task" "agent-name"
131132
bash scripts/agent-branch-start.sh "task" "agent-name"
132133
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
133134
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
@@ -157,6 +158,7 @@ Use this exact checklist to setup multi-agent safety in this repository for Code
157158
musafety doctor
158159
159160
4) Confirm next safe agent workflow commands:
161+
bash scripts/codex-agent.sh "task" "agent-name"
160162
bash scripts/agent-branch-start.sh "task" "agent-name"
161163
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
162164
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
@@ -275,13 +277,15 @@ multiagent.protectedBranches
275277
- risky stale/missing lock state
276278
- accidental loss of critical guardrail files
277279
- setup also writes a managed `.gitignore` block so generated musafety scripts/hooks stay out of normal git status noise by default
280+
- includes `oh-my-codex/` by default to keep local OMX source clones out of repo status
278281
- pass `--no-gitignore` if you want to keep tracking these files in git
279282

280283
## Files it installs
281284

282285
```text
283286
scripts/agent-branch-start.sh
284287
scripts/agent-branch-finish.sh
288+
scripts/codex-agent.sh
285289
scripts/agent-worktree-prune.sh
286290
scripts/agent-file-locks.py
287291
scripts/install-agent-git-hooks.sh

bin/multiagent-safety.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const TEMPLATE_ROOT = path.resolve(__dirname, '..', 'templates');
2727
const TEMPLATE_FILES = [
2828
'scripts/agent-branch-start.sh',
2929
'scripts/agent-branch-finish.sh',
30+
'scripts/codex-agent.sh',
3031
'scripts/agent-worktree-prune.sh',
3132
'scripts/agent-file-locks.py',
3233
'scripts/install-agent-git-hooks.sh',
@@ -39,6 +40,7 @@ const TEMPLATE_FILES = [
3940
const EXECUTABLE_RELATIVE_PATHS = new Set([
4041
'scripts/agent-branch-start.sh',
4142
'scripts/agent-branch-finish.sh',
43+
'scripts/codex-agent.sh',
4244
'scripts/agent-worktree-prune.sh',
4345
'scripts/agent-file-locks.py',
4446
'scripts/install-agent-git-hooks.sh',
@@ -61,11 +63,13 @@ const GITIGNORE_MARKER_END = '# multiagent-safety:END';
6163
const MANAGED_GITIGNORE_PATHS = [
6264
'scripts/agent-branch-start.sh',
6365
'scripts/agent-branch-finish.sh',
66+
'scripts/codex-agent.sh',
6467
'scripts/agent-worktree-prune.sh',
6568
'scripts/agent-file-locks.py',
6669
'scripts/install-agent-git-hooks.sh',
6770
'scripts/openspec/init-plan-workspace.sh',
6871
'.githooks/pre-commit',
72+
'oh-my-codex/',
6973
'.codex/skills/musafety/SKILL.md',
7074
'.claude/commands/musafety.md',
7175
LOCK_FILE_RELATIVE,
@@ -132,6 +136,7 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup multi-agent safety in
132136
musafety doctor
133137
134138
4) Confirm next safe agent workflow commands:
139+
bash scripts/codex-agent.sh "task" "agent-name"
135140
bash scripts/agent-branch-start.sh "task" "agent-name"
136141
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
137142
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
@@ -150,6 +155,7 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup multi-agent safety in
150155
const AI_SETUP_COMMANDS = `npm i -g musafety
151156
musafety setup
152157
musafety doctor
158+
bash scripts/codex-agent.sh "task" "agent-name"
153159
bash scripts/agent-branch-start.sh "task" "agent-name"
154160
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
155161
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
@@ -462,6 +468,7 @@ function ensurePackageScripts(repoRoot, dryRun) {
462468
}
463469

464470
const wantedScripts = {
471+
'agent:codex': 'bash ./scripts/codex-agent.sh',
465472
'agent:branch:start': 'bash ./scripts/agent-branch-start.sh',
466473
'agent:branch:finish': 'bash ./scripts/agent-branch-finish.sh',
467474
'agent:cleanup': 'bash ./scripts/agent-worktree-prune.sh --base dev',

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "musafety",
3-
"version": "0.4.7",
3+
"version": "0.4.9",
44
"description": "Simple setup command for hardened multi-agent collaboration safety in git repos.",
55
"license": "MIT",
66
"preferGlobal": true,

0 commit comments

Comments
 (0)