From 68c2fac980ff470bd5f237b7237f40cf5a9ee01c Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Sat, 11 Apr 2026 18:51:48 +0200 Subject: [PATCH] Make gx setup/init enforce a repeatable per-message agent branch lifecycle Users need guardrails to be obvious and repeatable on every task, not just first-run setup. This change makes setup/init surfaces explicitly teach and preserve the same branch->lock->PR/merge->cleanup loop, and introduces an init alias so teams can bootstrap with either command. Constraint: Existing setup behavior and template install paths must remain backward compatible Rejected: Hook-only enforcement without AGENTS/prompt updates | users still miss the workflow contract in agent instructions Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep setup prompt text, README checklist, and AGENTS template lifecycle rules aligned whenever workflow steps change Tested: node --check bin/multiagent-safety.js Tested: node --test --test-name-pattern="setup provisions workflow files and repo config" test/install.test.js Tested: node --test --test-name-pattern="init aliases setup and provisions workflow files" test/install.test.js Tested: temp repo bootstrap via node bin/multiagent-safety.js init --target --no-global-install with AGENTS loop assertions Tested: node bin/multiagent-safety.js copy-prompt | rg "gx init|For every new user message/task" Not-tested: full npm test remains noisy in this environment due pre-existing broad install/fuzz test instability unrelated to this change --- README.md | 8 ++++++++ bin/multiagent-safety.js | 13 ++++++++++++- templates/AGENTS.multiagent-safety.md | 2 ++ templates/codex/skills/guardex/SKILL.md | 3 +++ test/install.test.js | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fbaa333..ec1ff53 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ Related tools: ```sh # inside your repo gx setup +# alias: +gx init ``` That one command runs: @@ -217,6 +219,7 @@ Use this exact checklist to setup multi-agent safety in this repository for Code 2) Bootstrap safety in this repo: gx setup + # alias: gx init - Setup detects global OMX/OpenSpec first. - If one is missing and setup asks for approval, reply explicitly: @@ -231,6 +234,9 @@ Use this exact checklist to setup multi-agent safety in this repository for Code bash scripts/agent-branch-start.sh "task" "agent-name" python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" + - For every new user message/task, repeat the same cycle: + start isolated agent branch/worktree -> claim file locks -> implement/verify -> + finish via PR/merge cleanup with scripts/agent-branch-finish.sh. 5) Optional: create OpenSpec planning workspace: bash scripts/openspec/init-plan-workspace.sh "" @@ -251,6 +257,7 @@ Use this exact checklist to setup multi-agent safety in this repository for Code ```sh gx status [--target ] [--json] gx setup [--target ] [--dry-run] [--yes-global-install|--no-global-install] [--no-gitignore] +gx init [--target ] [--dry-run] [--yes-global-install|--no-global-install] [--no-gitignore] gx doctor [--target ] [--dry-run] [--json] [--keep-stale-locks] [--no-gitignore] gx copy-prompt gx copy-commands @@ -268,6 +275,7 @@ bash scripts/openspec/init-plan-workspace.sh # optional OpenSpec p No command defaults to `gx status` (non-mutating health/status view). `gx status` reports CLI/runtime info, global OMX/OpenSpec service status, and repo safety service state. +`gx init` is an alias of `gx setup`. When run in an interactive terminal, default `GuardeX` checks npm for a newer version first and asks `[y/N]` whether to update immediately (default is `N`). diff --git a/bin/multiagent-safety.js b/bin/multiagent-safety.js index d10c63b..a58420c 100755 --- a/bin/multiagent-safety.js +++ b/bin/multiagent-safety.js @@ -80,6 +80,7 @@ const COMMAND_TYPO_ALIASES = new Map([ ['realaese', 'release'], ['relase', 'release'], ['setpu', 'setup'], + ['inti', 'init'], ['intsall', 'install'], ['docter', 'doctor'], ['doctro', 'doctor'], @@ -88,6 +89,7 @@ const COMMAND_TYPO_ALIASES = new Map([ const SUGGESTIBLE_COMMANDS = [ 'status', 'setup', + 'init', 'doctor', 'report', 'copy-prompt', @@ -105,6 +107,7 @@ const SUGGESTIBLE_COMMANDS = [ const CLI_COMMAND_DESCRIPTIONS = [ ['status', 'Show GuardeX CLI + service health without modifying files'], ['setup', 'Install + repair guardrails in a git repo (supports --no-gitignore)'], + ['init', 'Alias of setup (bootstrap + repair guardrails in a git repo)'], ['doctor', 'Repair safety setup drift, then verify repo safety'], ['report', 'Generate security/safety reports (for example: OpenSSF scorecard)'], ['copy-prompt', 'Print the AI-ready setup checklist'], @@ -127,6 +130,7 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup GuardeX (Guardian T-R 2) Bootstrap safety in this repo: gx setup + # alias: gx init - Setup detects global OMX/OpenSpec first. - If one is missing and setup asks for approval, reply explicitly: @@ -141,6 +145,9 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup GuardeX (Guardian T-R bash scripts/agent-branch-start.sh "task" "agent-name" python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" + - For every new user message/task, repeat the same cycle: + start isolated agent branch/worktree -> claim file locks -> implement/verify -> + finish via PR/merge cleanup with scripts/agent-branch-finish.sh. 5) Optional: create OpenSpec planning workspace: bash scripts/openspec/init-plan-workspace.sh "" @@ -151,6 +158,9 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup GuardeX (Guardian T-R 7) Optional: sync your current agent branch with latest base branch: gx sync --check gx sync + +8) Optional (GitHub remote cleanup): enable: + Settings -> General -> Pull Requests -> Automatically delete head branches `; const AI_SETUP_COMMANDS = `npm i -g @imdeadpool/guardex @@ -272,6 +282,7 @@ ${commandCatalogLines().join('\n')} NOTES - Running ${TOOL_NAME} with no command defaults to: ${SHORT_TOOL_NAME} status - Short alias: ${SHORT_TOOL_NAME} + - ${SHORT_TOOL_NAME} init is an alias of ${SHORT_TOOL_NAME} setup - ${TOOL_NAME} setup asks for Y/N approval before global installs - Legacy command aliases are still supported: ${LEGACY_NAMES.join(', ')}`); @@ -2403,7 +2414,7 @@ function main() { return; } - if (command === 'setup') { + if (command === 'setup' || command === 'init') { setup(rest); return; } diff --git a/templates/AGENTS.multiagent-safety.md b/templates/AGENTS.multiagent-safety.md index 4c0ef44..bbce4a3 100644 --- a/templates/AGENTS.multiagent-safety.md +++ b/templates/AGENTS.multiagent-safety.md @@ -10,7 +10,9 @@ - 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 "" ""`. +- Treat the base branch (`main` or the user's current local base branch) as read-only while the agent branch is active. - Agent completion must use `scripts/agent-branch-finish.sh` (direct merge to base when allowed; auto PR fallback for protected bases, then cleanup after merge). +- Per-message loop is mandatory: for every new user message/task, start a fresh agent branch/worktree, claim ownership locks, implement and verify, finish via PR/merge cleanup, then repeat for the next message/task. 1. Explicit ownership before edits diff --git a/templates/codex/skills/guardex/SKILL.md b/templates/codex/skills/guardex/SKILL.md index efa491e..fbd3ab6 100644 --- a/templates/codex/skills/guardex/SKILL.md +++ b/templates/codex/skills/guardex/SKILL.md @@ -19,6 +19,8 @@ If guardrails are missing entirely, run: ```sh gx setup +# alias +gx init ``` Then verify: @@ -32,5 +34,6 @@ gx scan - Prefer `gx doctor` for one-step repair + verification. - Keep agent work isolated (`agent/*` branches + lock claims). +- For every new user message/task, restart the full loop on a fresh agent branch/worktree. - For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "" ""`. - Do not bypass protected branch safeguards unless explicitly required. diff --git a/test/install.test.js b/test/install.test.js index 52da8f8..38778c0 100644 --- a/test/install.test.js +++ b/test/install.test.js @@ -220,6 +220,7 @@ test('setup provisions workflow files and repo config', () => { const agentsContent = fs.readFileSync(path.join(repoDir, 'AGENTS.md'), 'utf8'); assert.equal(agentsContent.includes(''), true); + assert.match(agentsContent, /Per-message loop is mandatory/); const gitignoreContent = fs.readFileSync(path.join(repoDir, '.gitignore'), 'utf8'); assert.match(gitignoreContent, /# multiagent-safety:START/); @@ -241,6 +242,17 @@ test('setup provisions workflow files and repo config', () => { assert.equal(secondRun.status, 0, secondRun.stderr || secondRun.stdout); }); +test('init aliases setup and provisions workflow files', () => { + const repoDir = initRepo(); + + const result = runNode(['init', '--target', repoDir, '--no-global-install'], repoDir); + assert.equal(result.status, 0, result.stderr || result.stdout); + + assert.equal(fs.existsSync(path.join(repoDir, 'scripts', 'agent-branch-start.sh')), true); + assert.equal(fs.existsSync(path.join(repoDir, 'scripts', 'agent-branch-finish.sh')), true); + assert.equal(fs.existsSync(path.join(repoDir, 'AGENTS.md')), true); +}); + test('setup pre-commit blocks codex session commits on non-agent branches by default', () => { const repoDir = initRepo(); @@ -1187,8 +1199,10 @@ test('copy-prompt outputs AI setup instructions', () => { assert.match(result.stdout, /npm i -g @imdeadpool\/guardex/); assert.match(result.stdout, /npm i -g oh-my-codex @fission-ai\/openspec/); assert.match(result.stdout, /gx setup/); + assert.match(result.stdout, /gx init/); assert.match(result.stdout, /Codex or Claude/); assert.match(result.stdout, /scripts\/agent-file-locks.py claim/); + assert.match(result.stdout, /For every new user message\/task, repeat the same cycle/); }); test('copy-commands outputs command-only checklist', () => {