Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
.omx/
node_modules
oh-my-codex/

# multiagent-safety:START
scripts/agent-branch-start.sh
scripts/agent-branch-finish.sh
scripts/codex-agent.sh
scripts/agent-worktree-prune.sh
scripts/agent-file-locks.py
scripts/install-agent-git-hooks.sh
scripts/openspec/init-plan-workspace.sh
.githooks/pre-commit
oh-my-codex/
.codex/skills/musafety/SKILL.md
.claude/commands/musafety.md
.omx/state/agent-file-locks.json
# multiagent-safety:END
61 changes: 61 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,64 @@ OMX runtime state typically lives under `.omx/`:
- `.omx/project-memory.json`
- `.omx/plans/`
- `.omx/logs/`

<!-- multiagent-safety:START -->
## Multi-Agent Execution Contract (multiagent-safety)

0. Session plan comment + read gate (required)

- Before editing, each agent must post a short session comment/handoff note that includes:
- plan/change name (or checkpoint id),
- owned files/scope,
- intended action.
- Before deleting/replacing code, each agent must read the latest session comments/handoffs first and confirm the target code is in their owned scope.
- If ownership is unclear or overlaps, stop that edit, post a blocker comment, and let the leader/integrator reassign scope.
- For git isolation, each agent must start on a dedicated branch via `scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"`.
- Agent completion must use `scripts/agent-branch-finish.sh` (merge into `dev`, push, delete agent branch).

1. Explicit ownership before edits

- Assign each agent clear file/module ownership.
- Do not edit files outside your assigned scope unless the leader reassigns ownership.

2. Preserve parallel safety

- Assume other agents are editing nearby code concurrently.
- Never revert unrelated changes authored by others.
- If another change conflicts with your approach, adapt and report the conflict in handoff.

3. Verify before completion

- Run required local checks for the area you changed.
- Do not mark work complete without command output evidence.

4. Required handoff format (every agent)

- Files changed
- Behavior touched
- Verification commands + results
- Risks / follow-ups

## OpenSpec Plan Workspace (recommended)

When work needs a durable planning phase, scaffold a plan workspace before implementation:

```bash
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
```

Expected shape:

```text
openspec/plan/<plan-slug>/
summary.md
checkpoints.md
planner/plan.md
planner/tasks.md
architect/tasks.md
critic/tasks.md
executor/tasks.md
writer/tasks.md
verifier/tasks.md
```
<!-- multiagent-safety:END -->
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ bash scripts/openspec/init-plan-workspace.sh <plan-slug> # optional OpenSpec p
No command defaults to `musafety status` (non-mutating health/status view).
`musafety status` reports CLI/runtime info, global OMX/OpenSpec service status, and repo safety service state.
When run in an interactive terminal, default `musafety` checks npm for a newer version first
and asks `[Y/n]` whether to update immediately (default is `Y`).
and asks `[y/N]` whether to update immediately (default is `N`).

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

- direct commits to protected branches (defaults: `dev`, `main`, `master`; configurable via `musafety protect ...`)
- protected-branch commits are blocked regardless of commit client (including VS Code Source Control)
- Codex-session commits on non-`agent/*` branches are blocked by default (`multiagent.codexRequireAgentBranch=true`)
- overlapping file ownership between agents
- unapproved deletions of claimed files
- risky stale/missing lock state
- accidental loss of critical guardrail files
- in-place branch bootstrap requires explicit opt-in (`--in-place --allow-in-place`)
- setup also writes a managed `.gitignore` block so generated musafety scripts/hooks stay out of normal git status noise by default
- includes `oh-my-codex/` by default to keep local OMX source clones out of repo status
- pass `--no-gitignore` if you want to keep tracking these files in git
Expand Down
2 changes: 1 addition & 1 deletion bin/multiagent-safety.js
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ function maybeSelfUpdateBeforeStatus() {
? autoApproval
: promptYesNo(
`Update now? (${NPM_BIN} i -g ${packageJson.name}@latest)`,
true,
false,
);

if (!shouldUpdate) {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 19 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "musafety",
"version": "0.4.9",
"version": "5.0.0",
"description": "Simple setup command for hardened multi-agent collaboration safety in git repos.",
"license": "MIT",
"preferGlobal": true,
Expand All @@ -9,7 +9,24 @@
"multiagent-safety": "bin/multiagent-safety.js"
},
"scripts": {
"test": "node --test test/*.test.js"
"test": "node --test test/*.test.js",
"agent:codex": "bash ./scripts/codex-agent.sh",
"agent:branch:start": "bash ./scripts/agent-branch-start.sh",
"agent:branch:finish": "bash ./scripts/agent-branch-finish.sh",
"agent:cleanup": "bash ./scripts/agent-worktree-prune.sh --base dev",
"agent:hooks:install": "bash ./scripts/install-agent-git-hooks.sh",
"agent:locks:claim": "python3 ./scripts/agent-file-locks.py claim",
"agent:locks:allow-delete": "python3 ./scripts/agent-file-locks.py allow-delete",
"agent:locks:release": "python3 ./scripts/agent-file-locks.py release",
"agent:locks:status": "python3 ./scripts/agent-file-locks.py status",
"agent:plan:init": "bash ./scripts/openspec/init-plan-workspace.sh",
"agent:protect:list": "musafety protect list",
"agent:branch:sync": "musafety sync",
"agent:branch:sync:check": "musafety sync --check",
"agent:safety:setup": "musafety setup",
"agent:safety:scan": "musafety scan",
"agent:safety:fix": "musafety fix",
"agent:safety:doctor": "musafety doctor"
},
"engines": {
"node": ">=18"
Expand Down
1 change: 1 addition & 0 deletions templates/codex/skills/musafety/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ musafety scan

- Prefer `musafety doctor` for one-step repair + verification.
- Keep agent work isolated (`agent/*` branches + lock claims).
- For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "<task>" "<agent-name>"`.
- Do not bypass protected branch safeguards unless explicitly required.
35 changes: 35 additions & 0 deletions templates/githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,41 @@ MSG
exit 1
fi

codex_require_agent_branch_raw="${MUSAFETY_CODEX_REQUIRE_AGENT_BRANCH:-$(git config --get multiagent.codexRequireAgentBranch || true)}"
if [[ -z "$codex_require_agent_branch_raw" ]]; then
codex_require_agent_branch_raw="true"
fi
codex_require_agent_branch="$(printf '%s' "$codex_require_agent_branch_raw" | tr '[:upper:]' '[:lower:]')"

should_require_codex_agent_branch=0
case "$codex_require_agent_branch" in
1|true|yes|on) should_require_codex_agent_branch=1 ;;
0|false|no|off) should_require_codex_agent_branch=0 ;;
*) should_require_codex_agent_branch=1 ;;
esac

if [[ "$should_require_codex_agent_branch" == "1" && "${MUSAFETY_ALLOW_CODEX_ON_NON_AGENT:-0}" != "1" ]]; then
is_codex_session=0
if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}" == "1" ]]; then
is_codex_session=1
fi

if [[ "$is_codex_session" == "1" && "$branch" != agent/* ]]; then
cat >&2 <<'MSG'
[codex-branch-guard] Codex agent commit blocked on non-agent branch.
Use isolated branch/worktree first:
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
Then commit from the created agent/* branch.

Temporary bypass (not recommended):
MUSAFETY_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
Disable this rule for a repo (not recommended):
git config multiagent.codexRequireAgentBranch false
MSG
exit 1
fi
fi

if [[ "$branch" == agent/* ]]; then
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
cat >&2 <<'MSG'
Expand Down
45 changes: 40 additions & 5 deletions templates/scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail

TASK_NAME="${1:-task}"
AGENT_NAME="${2:-agent}"
BASE_BRANCH="${3:-dev}"
TASK_NAME="task"
AGENT_NAME="agent"
BASE_BRANCH="dev"
WORKTREE_MODE=1
ALLOW_IN_PLACE=0
WORKTREE_ROOT_REL=".omx/agent-worktrees"
POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
case "$1" in
Expand All @@ -25,25 +27,52 @@ while [[ $# -gt 0 ]]; do
WORKTREE_MODE=0
shift
;;
--allow-in-place)
ALLOW_IN_PLACE=1
shift
;;
--worktree-root)
WORKTREE_ROOT_REL="${2:-.omx/agent-worktrees}"
shift 2
;;
--)
shift
while [[ $# -gt 0 ]]; do
POSITIONAL_ARGS+=("$1")
shift
done
break
;;
-*)
echo "[agent-branch-start] Unknown option: $1" >&2
echo "Usage: $0 [task] [agent] [base] [--in-place] [--worktree-root <path>]" >&2
echo "Usage: $0 [task] [agent] [base] [--in-place --allow-in-place] [--worktree-root <path>]" >&2
exit 1
;;
*)
break
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done

if [[ "${#POSITIONAL_ARGS[@]}" -gt 3 ]]; then
echo "[agent-branch-start] Too many positional arguments." >&2
echo "Usage: $0 [task] [agent] [base] [--in-place --allow-in-place] [--worktree-root <path>]" >&2
exit 1
fi

if [[ "${#POSITIONAL_ARGS[@]}" -ge 1 ]]; then
TASK_NAME="${POSITIONAL_ARGS[0]}"
fi

if [[ "${#POSITIONAL_ARGS[@]}" -ge 2 ]]; then
AGENT_NAME="${POSITIONAL_ARGS[1]}"
fi

if [[ "${#POSITIONAL_ARGS[@]}" -ge 3 ]]; then
BASE_BRANCH="${POSITIONAL_ARGS[2]}"
fi

sanitize_slug() {
local raw="$1"
local slug
Expand Down Expand Up @@ -83,6 +112,12 @@ if git show-ref --verify --quiet "refs/heads/${branch_name}"; then
fi

if [[ "$WORKTREE_MODE" -eq 0 ]]; then
if [[ "$ALLOW_IN_PLACE" -ne 1 ]]; then
echo "[agent-branch-start] --in-place is blocked by default to prevent accidental edits on protected branches." >&2
echo "[agent-branch-start] If you really need it, pass both: --in-place --allow-in-place" >&2
exit 1
fi

if ! git diff --quiet || ! git diff --cached --quiet; then
echo "[agent-branch-start] Working tree is not clean. Commit/stash changes before starting an in-place branch." >&2
exit 1
Expand Down
49 changes: 43 additions & 6 deletions templates/scripts/codex-agent.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail

TASK_NAME="${1:-task}"
AGENT_NAME="${2:-agent}"
BASE_BRANCH="${3:-dev}"
TASK_NAME="${MUSAFETY_TASK_NAME:-task}"
AGENT_NAME="${MUSAFETY_AGENT_NAME:-agent}"
BASE_BRANCH="${MUSAFETY_BASE_BRANCH:-dev}"
CODEX_BIN="${MUSAFETY_CODEX_BIN:-codex}"

if [[ $# -ge 1 ]]; then shift; fi
if [[ $# -ge 1 ]]; then shift; fi
if [[ $# -ge 1 ]]; then shift; fi
while [[ $# -gt 0 ]]; do
case "$1" in
--task)
TASK_NAME="${2:-$TASK_NAME}"
shift 2
;;
--agent)
AGENT_NAME="${2:-$AGENT_NAME}"
shift 2
;;
--base)
BASE_BRANCH="${2:-$BASE_BRANCH}"
shift 2
;;
--codex-bin)
CODEX_BIN="${2:-$CODEX_BIN}"
shift 2
;;
--)
shift
break
;;
-*)
break
;;
*)
TASK_NAME="$1"
shift
if [[ $# -gt 0 && "${1:-}" != -* ]]; then
AGENT_NAME="$1"
shift
fi
if [[ $# -gt 0 && "${1:-}" != -* ]]; then
BASE_BRANCH="$1"
shift
fi
break
;;
esac
done

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