Skip to content

Commit 14f505e

Browse files
NagyViktNagyVikt
andauthored
Keep Codex workflow changes isolated and safer by default (#12)
This captures the pending musafety workflow hardening: Codex session commits now require agent/* branches by default, startup sandbox scripts gained stronger argument handling, and status self-update prompts default to no. The change also updates docs/templates/tests and publishes the next package version. Constraint: Protected branches must stay safe for interactive maintainers while still supporting sandboxed agent automation Rejected: Keep Codex non-agent commits allowed by default | too easy to accidentally write from protected/non-isolated branches Confidence: high Scope-risk: moderate Reversibility: clean Directive: If relaxing branch guards, update both hook templates and install tests in the same change Tested: npm test (41/41 pass) Not-tested: end-to-end npm publish in CI for this exact commit Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
1 parent 401ccda commit 14f505e

11 files changed

Lines changed: 316 additions & 18 deletions

File tree

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
11
.omx/
22
node_modules
33
oh-my-codex/
4+
5+
# multiagent-safety:START
6+
scripts/agent-branch-start.sh
7+
scripts/agent-branch-finish.sh
8+
scripts/codex-agent.sh
9+
scripts/agent-worktree-prune.sh
10+
scripts/agent-file-locks.py
11+
scripts/install-agent-git-hooks.sh
12+
scripts/openspec/init-plan-workspace.sh
13+
.githooks/pre-commit
14+
oh-my-codex/
15+
.codex/skills/musafety/SKILL.md
16+
.claude/commands/musafety.md
17+
.omx/state/agent-file-locks.json
18+
# multiagent-safety:END

AGENTS.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,64 @@ OMX runtime state typically lives under `.omx/`:
7474
- `.omx/project-memory.json`
7575
- `.omx/plans/`
7676
- `.omx/logs/`
77+
78+
<!-- multiagent-safety:START -->
79+
## Multi-Agent Execution Contract (multiagent-safety)
80+
81+
0. Session plan comment + read gate (required)
82+
83+
- Before editing, each agent must post a short session comment/handoff note that includes:
84+
- plan/change name (or checkpoint id),
85+
- owned files/scope,
86+
- intended action.
87+
- Before deleting/replacing code, each agent must read the latest session comments/handoffs first and confirm the target code is in their owned scope.
88+
- If ownership is unclear or overlaps, stop that edit, post a blocker comment, and let the leader/integrator reassign scope.
89+
- For git isolation, each agent must start on a dedicated branch via `scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"`.
90+
- Agent completion must use `scripts/agent-branch-finish.sh` (merge into `dev`, push, delete agent branch).
91+
92+
1. Explicit ownership before edits
93+
94+
- Assign each agent clear file/module ownership.
95+
- Do not edit files outside your assigned scope unless the leader reassigns ownership.
96+
97+
2. Preserve parallel safety
98+
99+
- Assume other agents are editing nearby code concurrently.
100+
- Never revert unrelated changes authored by others.
101+
- If another change conflicts with your approach, adapt and report the conflict in handoff.
102+
103+
3. Verify before completion
104+
105+
- Run required local checks for the area you changed.
106+
- Do not mark work complete without command output evidence.
107+
108+
4. Required handoff format (every agent)
109+
110+
- Files changed
111+
- Behavior touched
112+
- Verification commands + results
113+
- Risks / follow-ups
114+
115+
## OpenSpec Plan Workspace (recommended)
116+
117+
When work needs a durable planning phase, scaffold a plan workspace before implementation:
118+
119+
```bash
120+
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
121+
```
122+
123+
Expected shape:
124+
125+
```text
126+
openspec/plan/<plan-slug>/
127+
summary.md
128+
checkpoints.md
129+
planner/plan.md
130+
planner/tasks.md
131+
architect/tasks.md
132+
critic/tasks.md
133+
executor/tasks.md
134+
writer/tasks.md
135+
verifier/tasks.md
136+
```
137+
<!-- multiagent-safety:END -->

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ bash scripts/openspec/init-plan-workspace.sh <plan-slug> # optional OpenSpec p
197197
No command defaults to `musafety status` (non-mutating health/status view).
198198
`musafety status` reports CLI/runtime info, global OMX/OpenSpec service status, and repo safety service state.
199199
When run in an interactive terminal, default `musafety` checks npm for a newer version first
200-
and asks `[Y/n]` whether to update immediately (default is `Y`).
200+
and asks `[y/N]` whether to update immediately (default is `N`).
201201

202202
- Interactive setup: prompts for Y/N approval before global OMX/OpenSpec install.
203203
- Interactive prompt is strict (`[y/n]`) and waits for explicit answer.
@@ -272,10 +272,12 @@ multiagent.protectedBranches
272272

273273
- direct commits to protected branches (defaults: `dev`, `main`, `master`; configurable via `musafety protect ...`)
274274
- protected-branch commits are blocked regardless of commit client (including VS Code Source Control)
275+
- Codex-session commits on non-`agent/*` branches are blocked by default (`multiagent.codexRequireAgentBranch=true`)
275276
- overlapping file ownership between agents
276277
- unapproved deletions of claimed files
277278
- risky stale/missing lock state
278279
- accidental loss of critical guardrail files
280+
- in-place branch bootstrap requires explicit opt-in (`--in-place --allow-in-place`)
279281
- setup also writes a managed `.gitignore` block so generated musafety scripts/hooks stay out of normal git status noise by default
280282
- includes `oh-my-codex/` by default to keep local OMX source clones out of repo status
281283
- pass `--no-gitignore` if you want to keep tracking these files in git

bin/multiagent-safety.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,7 @@ function maybeSelfUpdateBeforeStatus() {
12661266
? autoApproval
12671267
: promptYesNo(
12681268
`Update now? (${NPM_BIN} i -g ${packageJson.name}@latest)`,
1269-
true,
1269+
false,
12701270
);
12711271

12721272
if (!shouldUpdate) {

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: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "musafety",
3-
"version": "0.4.9",
3+
"version": "5.0.0",
44
"description": "Simple setup command for hardened multi-agent collaboration safety in git repos.",
55
"license": "MIT",
66
"preferGlobal": true,
@@ -9,7 +9,24 @@
99
"multiagent-safety": "bin/multiagent-safety.js"
1010
},
1111
"scripts": {
12-
"test": "node --test test/*.test.js"
12+
"test": "node --test test/*.test.js",
13+
"agent:codex": "bash ./scripts/codex-agent.sh",
14+
"agent:branch:start": "bash ./scripts/agent-branch-start.sh",
15+
"agent:branch:finish": "bash ./scripts/agent-branch-finish.sh",
16+
"agent:cleanup": "bash ./scripts/agent-worktree-prune.sh --base dev",
17+
"agent:hooks:install": "bash ./scripts/install-agent-git-hooks.sh",
18+
"agent:locks:claim": "python3 ./scripts/agent-file-locks.py claim",
19+
"agent:locks:allow-delete": "python3 ./scripts/agent-file-locks.py allow-delete",
20+
"agent:locks:release": "python3 ./scripts/agent-file-locks.py release",
21+
"agent:locks:status": "python3 ./scripts/agent-file-locks.py status",
22+
"agent:plan:init": "bash ./scripts/openspec/init-plan-workspace.sh",
23+
"agent:protect:list": "musafety protect list",
24+
"agent:branch:sync": "musafety sync",
25+
"agent:branch:sync:check": "musafety sync --check",
26+
"agent:safety:setup": "musafety setup",
27+
"agent:safety:scan": "musafety scan",
28+
"agent:safety:fix": "musafety fix",
29+
"agent:safety:doctor": "musafety doctor"
1330
},
1431
"engines": {
1532
"node": ">=18"

templates/codex/skills/musafety/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ musafety scan
3232

3333
- Prefer `musafety doctor` for one-step repair + verification.
3434
- Keep agent work isolated (`agent/*` branches + lock claims).
35+
- For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "<task>" "<agent-name>"`.
3536
- Do not bypass protected branch safeguards unless explicitly required.

templates/githooks/pre-commit

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,41 @@ MSG
4343
exit 1
4444
fi
4545

46+
codex_require_agent_branch_raw="${MUSAFETY_CODEX_REQUIRE_AGENT_BRANCH:-$(git config --get multiagent.codexRequireAgentBranch || true)}"
47+
if [[ -z "$codex_require_agent_branch_raw" ]]; then
48+
codex_require_agent_branch_raw="true"
49+
fi
50+
codex_require_agent_branch="$(printf '%s' "$codex_require_agent_branch_raw" | tr '[:upper:]' '[:lower:]')"
51+
52+
should_require_codex_agent_branch=0
53+
case "$codex_require_agent_branch" in
54+
1|true|yes|on) should_require_codex_agent_branch=1 ;;
55+
0|false|no|off) should_require_codex_agent_branch=0 ;;
56+
*) should_require_codex_agent_branch=1 ;;
57+
esac
58+
59+
if [[ "$should_require_codex_agent_branch" == "1" && "${MUSAFETY_ALLOW_CODEX_ON_NON_AGENT:-0}" != "1" ]]; then
60+
is_codex_session=0
61+
if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}" == "1" ]]; then
62+
is_codex_session=1
63+
fi
64+
65+
if [[ "$is_codex_session" == "1" && "$branch" != agent/* ]]; then
66+
cat >&2 <<'MSG'
67+
[codex-branch-guard] Codex agent commit blocked on non-agent branch.
68+
Use isolated branch/worktree first:
69+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
70+
Then commit from the created agent/* branch.
71+
72+
Temporary bypass (not recommended):
73+
MUSAFETY_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
74+
Disable this rule for a repo (not recommended):
75+
git config multiagent.codexRequireAgentBranch false
76+
MSG
77+
exit 1
78+
fi
79+
fi
80+
4681
if [[ "$branch" == agent/* ]]; then
4782
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
4883
cat >&2 <<'MSG'

templates/scripts/agent-branch-start.sh

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
TASK_NAME="${1:-task}"
5-
AGENT_NAME="${2:-agent}"
6-
BASE_BRANCH="${3:-dev}"
4+
TASK_NAME="task"
5+
AGENT_NAME="agent"
6+
BASE_BRANCH="dev"
77
WORKTREE_MODE=1
8+
ALLOW_IN_PLACE=0
89
WORKTREE_ROOT_REL=".omx/agent-worktrees"
10+
POSITIONAL_ARGS=()
911

1012
while [[ $# -gt 0 ]]; do
1113
case "$1" in
@@ -25,25 +27,52 @@ while [[ $# -gt 0 ]]; do
2527
WORKTREE_MODE=0
2628
shift
2729
;;
30+
--allow-in-place)
31+
ALLOW_IN_PLACE=1
32+
shift
33+
;;
2834
--worktree-root)
2935
WORKTREE_ROOT_REL="${2:-.omx/agent-worktrees}"
3036
shift 2
3137
;;
3238
--)
3339
shift
40+
while [[ $# -gt 0 ]]; do
41+
POSITIONAL_ARGS+=("$1")
42+
shift
43+
done
3444
break
3545
;;
3646
-*)
3747
echo "[agent-branch-start] Unknown option: $1" >&2
38-
echo "Usage: $0 [task] [agent] [base] [--in-place] [--worktree-root <path>]" >&2
48+
echo "Usage: $0 [task] [agent] [base] [--in-place --allow-in-place] [--worktree-root <path>]" >&2
3949
exit 1
4050
;;
4151
*)
42-
break
52+
POSITIONAL_ARGS+=("$1")
53+
shift
4354
;;
4455
esac
4556
done
4657

58+
if [[ "${#POSITIONAL_ARGS[@]}" -gt 3 ]]; then
59+
echo "[agent-branch-start] Too many positional arguments." >&2
60+
echo "Usage: $0 [task] [agent] [base] [--in-place --allow-in-place] [--worktree-root <path>]" >&2
61+
exit 1
62+
fi
63+
64+
if [[ "${#POSITIONAL_ARGS[@]}" -ge 1 ]]; then
65+
TASK_NAME="${POSITIONAL_ARGS[0]}"
66+
fi
67+
68+
if [[ "${#POSITIONAL_ARGS[@]}" -ge 2 ]]; then
69+
AGENT_NAME="${POSITIONAL_ARGS[1]}"
70+
fi
71+
72+
if [[ "${#POSITIONAL_ARGS[@]}" -ge 3 ]]; then
73+
BASE_BRANCH="${POSITIONAL_ARGS[2]}"
74+
fi
75+
4776
sanitize_slug() {
4877
local raw="$1"
4978
local slug
@@ -83,6 +112,12 @@ if git show-ref --verify --quiet "refs/heads/${branch_name}"; then
83112
fi
84113

85114
if [[ "$WORKTREE_MODE" -eq 0 ]]; then
115+
if [[ "$ALLOW_IN_PLACE" -ne 1 ]]; then
116+
echo "[agent-branch-start] --in-place is blocked by default to prevent accidental edits on protected branches." >&2
117+
echo "[agent-branch-start] If you really need it, pass both: --in-place --allow-in-place" >&2
118+
exit 1
119+
fi
120+
86121
if ! git diff --quiet || ! git diff --cached --quiet; then
87122
echo "[agent-branch-start] Working tree is not clean. Commit/stash changes before starting an in-place branch." >&2
88123
exit 1

templates/scripts/codex-agent.sh

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
TASK_NAME="${1:-task}"
5-
AGENT_NAME="${2:-agent}"
6-
BASE_BRANCH="${3:-dev}"
4+
TASK_NAME="${MUSAFETY_TASK_NAME:-task}"
5+
AGENT_NAME="${MUSAFETY_AGENT_NAME:-agent}"
6+
BASE_BRANCH="${MUSAFETY_BASE_BRANCH:-dev}"
77
CODEX_BIN="${MUSAFETY_CODEX_BIN:-codex}"
88

9-
if [[ $# -ge 1 ]]; then shift; fi
10-
if [[ $# -ge 1 ]]; then shift; fi
11-
if [[ $# -ge 1 ]]; then shift; fi
9+
while [[ $# -gt 0 ]]; do
10+
case "$1" in
11+
--task)
12+
TASK_NAME="${2:-$TASK_NAME}"
13+
shift 2
14+
;;
15+
--agent)
16+
AGENT_NAME="${2:-$AGENT_NAME}"
17+
shift 2
18+
;;
19+
--base)
20+
BASE_BRANCH="${2:-$BASE_BRANCH}"
21+
shift 2
22+
;;
23+
--codex-bin)
24+
CODEX_BIN="${2:-$CODEX_BIN}"
25+
shift 2
26+
;;
27+
--)
28+
shift
29+
break
30+
;;
31+
-*)
32+
break
33+
;;
34+
*)
35+
TASK_NAME="$1"
36+
shift
37+
if [[ $# -gt 0 && "${1:-}" != -* ]]; then
38+
AGENT_NAME="$1"
39+
shift
40+
fi
41+
if [[ $# -gt 0 && "${1:-}" != -* ]]; then
42+
BASE_BRANCH="$1"
43+
shift
44+
fi
45+
break
46+
;;
47+
esac
48+
done
1249

1350
if ! command -v "$CODEX_BIN" >/dev/null 2>&1; then
1451
echo "[codex-agent] Missing Codex CLI command: $CODEX_BIN" >&2

0 commit comments

Comments
 (0)