From 1e1d2e7f39ab78e8bb687cb98f1fc3e8f49a4d69 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Mon, 13 Apr 2026 21:29:57 +0200 Subject: [PATCH] Keep AGENTS branch-worktree policy current across repos during setup Setup previously stopped updating AGENTS.md once the managed marker existed, so older repos could keep outdated branch-handling rules. This change refreshes the marker-bounded block from the latest template and updates the managed policy text to explicitly require sub-branch reuse/new creation without touching the local base checkout.\n\nConstraint: Preserve user-authored AGENTS content outside the multiagent managed marker block\nRejected: Rewrite full AGENTS.md file on every setup run | would overwrite repo-specific instructions\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Keep marker-bounded replacement logic aligned with template marker names when evolving AGENTS policy text\nTested: npm test; node --check bin/multiagent-safety.js; npm pack --dry-run\nNot-tested: Manual run against a repository containing malformed AGENTS markers (start without end) --- bin/multiagent-safety.js | 18 +++++++++++++- templates/AGENTS.multiagent-safety.md | 3 ++- test/install.test.js | 34 ++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/bin/multiagent-safety.js b/bin/multiagent-safety.js index f6ae168..977b22a 100755 --- a/bin/multiagent-safety.js +++ b/bin/multiagent-safety.js @@ -79,6 +79,7 @@ const CRITICAL_GUARDRAIL_PATHS = new Set([ const LOCK_FILE_RELATIVE = '.omx/state/agent-file-locks.json'; const AGENTS_MARKER_START = ''; +const AGENTS_MARKER_END = ''; const GITIGNORE_MARKER_START = '# multiagent-safety:START'; const GITIGNORE_MARKER_END = '# multiagent-safety:END'; const MANAGED_GITIGNORE_PATHS = [ @@ -608,6 +609,10 @@ function ensurePackageScripts(repoRoot, dryRun) { function ensureAgentsSnippet(repoRoot, dryRun) { const agentsPath = path.join(repoRoot, 'AGENTS.md'); const snippet = fs.readFileSync(path.join(TEMPLATE_ROOT, 'AGENTS.multiagent-safety.md'), 'utf8').trimEnd(); + const managedRegex = new RegExp( + `${AGENTS_MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${AGENTS_MARKER_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, + 'm', + ); if (!fs.existsSync(agentsPath)) { if (!dryRun) { @@ -617,8 +622,19 @@ function ensureAgentsSnippet(repoRoot, dryRun) { } const existing = fs.readFileSync(agentsPath, 'utf8'); + if (managedRegex.test(existing)) { + const next = existing.replace(managedRegex, snippet); + if (next === existing) { + return { status: 'unchanged', file: 'AGENTS.md' }; + } + if (!dryRun) { + fs.writeFileSync(agentsPath, next, 'utf8'); + } + return { status: 'updated', file: 'AGENTS.md', note: 'refreshed guardex-managed block' }; + } + if (existing.includes(AGENTS_MARKER_START)) { - return { status: 'unchanged', file: 'AGENTS.md' }; + return { status: 'unchanged', file: 'AGENTS.md', note: 'existing marker found without managed end marker' }; } const separator = existing.endsWith('\n') ? '\n' : '\n\n'; diff --git a/templates/AGENTS.multiagent-safety.md b/templates/AGENTS.multiagent-safety.md index c39b81c..d833654 100644 --- a/templates/AGENTS.multiagent-safety.md +++ b/templates/AGENTS.multiagent-safety.md @@ -17,7 +17,8 @@ - If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "" --via-pr --wait-for-merge` and keep the branch open until checks/review pass. - If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --via-pr` until merged. - Completion is not valid until these are true: commit exists on the agent branch, branch is pushed to `origin`, and PR/merge status is produced by `agent-branch-finish.sh` or `codex-agent`. -- 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. +- For every new task, if an assigned agent sub-branch/worktree is already open, continue in that sub-branch; otherwise create a fresh one from the current local base snapshot with `scripts/agent-branch-start.sh`. +- Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree. - If the change publishes or bumps a version, the same change must also update release notes/changelog entries. 1. Explicit ownership before edits diff --git a/test/install.test.js b/test/install.test.js index ebc3010..d8e0215 100644 --- a/test/install.test.js +++ b/test/install.test.js @@ -247,7 +247,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/); + assert.match(agentsContent, /For every new task, if an assigned agent sub-branch\/worktree is already open, continue in that sub-branch/); const gitignoreContent = fs.readFileSync(path.join(repoDir, '.gitignore'), 'utf8'); assert.match(gitignoreContent, /# multiagent-safety:START/); @@ -271,6 +271,38 @@ test('setup provisions workflow files and repo config', () => { assert.equal(secondRun.status, 0, secondRun.stderr || secondRun.stdout); }); +test('setup refreshes existing managed AGENTS block to latest template policy', () => { + const repoDir = initRepo(); + const legacyAgents = `# AGENTS + +Project-specific guidance before managed block. + + +## Multi-Agent Execution Contract (multiagent-safety) +- legacy managed clause + + +Trailing project notes after managed block. +`; + fs.writeFileSync(path.join(repoDir, 'AGENTS.md'), legacyAgents, 'utf8'); + + const result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir); + assert.equal(result.status, 0, result.stderr || result.stdout); + + const nextAgents = fs.readFileSync(path.join(repoDir, 'AGENTS.md'), 'utf8'); + assert.match(nextAgents, /Project-specific guidance before managed block\./); + assert.match(nextAgents, /Trailing project notes after managed block\./); + assert.match( + nextAgents, + /For every new task, if an assigned agent sub-branch\/worktree is already open, continue in that sub-branch/, + ); + assert.match( + nextAgents, + /Never implement directly on the local\/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch\/worktree\./, + ); + assert.doesNotMatch(nextAgents, /legacy managed clause/); +}); + test('setup auto-adds existing local user branches to protected branches', () => { const repoDir = initRepo();