From 34bfc0dde40dc06aaab184224e4519709575f0bd Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Wed, 15 Apr 2026 08:49:08 +0200 Subject: [PATCH] Ship probot template sync and guardex skill-merge workflow This packages the pending branch changes from the agent worktree, including the new merge-skill runbook and related installer/test updates, so the branch can be finalized through PR merge flow. Constraint: Must preserve multiagent-safety protected-branch workflow and template propagation behavior Rejected: Direct commit on base branch | Violates guarded branch workflow Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep SKILL template wiring in sync between templates and installed .codex skill paths Tested: node --test test/install.test.js Not-tested: Manual GitHub UI verification of template usage after setup in an external repository --- .../guardex-merge-skills-to-dev/SKILL.md | 58 +++++++++++++++++++ .codex/skills/guardex/SKILL.md | 48 +-------------- .github/workflows/cr.yml | 21 +++++++ README.md | 36 ++++++++++++ bin/multiagent-safety.js | 27 +++++++++ .../guardex-merge-skills-to-dev/SKILL.md | 58 +++++++++++++++++++ templates/codex/skills/guardex/SKILL.md | 1 + templates/github/pull.yml.example | 6 ++ templates/github/workflows/cr.yml | 21 +++++++ test/install.test.js | 20 +++++-- 10 files changed, 244 insertions(+), 52 deletions(-) create mode 100644 .codex/skills/guardex-merge-skills-to-dev/SKILL.md create mode 100644 .github/workflows/cr.yml create mode 100644 templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md create mode 100644 templates/github/pull.yml.example create mode 100644 templates/github/workflows/cr.yml diff --git a/.codex/skills/guardex-merge-skills-to-dev/SKILL.md b/.codex/skills/guardex-merge-skills-to-dev/SKILL.md new file mode 100644 index 0000000..7e2d13e --- /dev/null +++ b/.codex/skills/guardex-merge-skills-to-dev/SKILL.md @@ -0,0 +1,58 @@ +--- +name: guardex-merge-skills-to-dev +description: "Use when you need to merge SKILL.md updates from agent branches/worktrees into the local base branch (default: dev) with the multiagent-safety flow." +--- + +# GuardeX Merge Skills to dev + +Use this skill when you only want to promote Codex skill file updates into the base branch (normally `dev`) without editing the visible base checkout directly. + +## What this merges + +- `.codex/skills/**/SKILL.md` +- `templates/codex/skills/**/SKILL.md` + +## Merge runbook (safe path) + +1. Resolve the base branch: + +```sh +BASE_BRANCH="$(git config --get multiagent.baseBranch || echo dev)" +echo "$BASE_BRANCH" +``` + +2. Start a dedicated integration sandbox from base: + +```sh +bash scripts/agent-branch-start.sh "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH" +``` + +3. Enter the sandbox worktree printed by the command above. + +4. Pull only skill files from each source agent branch: + +```sh +SOURCE_BRANCH="" +git checkout "$SOURCE_BRANCH" -- ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md' +``` + +5. Verify scope before commit: + +```sh +git status --short +git diff --name-only +``` + +6. Commit and merge back to base using guardex finish flow: + +```sh +git add .codex/skills templates/codex/skills +git commit -m "Merge skill file updates into ${BASE_BRANCH}" +bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup +``` + +## Notes + +- If a source branch has non-skill changes, this runbook keeps them out of the merge. +- If merge conflicts occur, resolve only within the skill files, then rerun `agent-branch-finish.sh`. +- Do not commit directly on `dev`/`main`; always merge through an agent branch/worktree. diff --git a/.codex/skills/guardex/SKILL.md b/.codex/skills/guardex/SKILL.md index f1e5d05..31a8142 100644 --- a/.codex/skills/guardex/SKILL.md +++ b/.codex/skills/guardex/SKILL.md @@ -33,51 +33,5 @@ gx scan - Prefer `gx 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 "" ""`. +- For skill-file-only merges into the local base branch (`dev` by default), use `$guardex-merge-skills-to-dev`. - Do not bypass protected branch safeguards unless explicitly required. - -## Bulk merge runbook (changed agent branches) - -Use this when a repo has many `agent/*` branches/worktrees with pending changes and you need them merged into the base branch quickly. - -1. Confirm base and guardrails are healthy: - -```sh -git status --short --branch -git pull --ff-only origin "$(git config --get multiagent.baseBranch || echo dev)" -gx scan -``` - -2. Run bulk finish first: - -```sh -gx finish --all -``` - -3. If a branch fails with `already used by worktree` or stale rebase hints, clear stale state in that worktree, then retry targeted finish: - -```sh -git -C "" rebase --abort || true -gx finish --branch "" --base "$(git config --get multiagent.baseBranch || echo dev)" --no-wait-for-merge --cleanup -``` - -4. If `gh pr merge` exits non-zero due local branch deletion but PR is already merged, verify with: - -```sh -gh pr view "" --json state,mergedAt,url -``` - -5. If a branch is still ahead of base with no open PR, create and merge a follow-up PR manually: - -```sh -gh pr create --base "" --head "" --title "Auto-finish: " --body "Follow-up merge for pending branch commits." -gh pr merge "" --squash --delete-branch -``` - -6. Final verification: - -```sh -gh pr list --state open --search "head:agent/ base:" -git pull --ff-only origin "" -gx cleanup -gx scan -``` diff --git a/.github/workflows/cr.yml b/.github/workflows/cr.yml new file mode 100644 index 0000000..2fc2e8e --- /dev/null +++ b/.github/workflows/cr.yml @@ -0,0 +1,21 @@ +name: Code Review + +on: + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + +jobs: + review: + if: ${{ secrets.OPENAI_API_KEY != '' }} + runs-on: ubuntu-latest + steps: + - uses: anc95/ChatGPT-CodeReview@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENAI_API_ENDPOINT: https://api.openai.com/v1 + MODEL: gpt-4o-mini diff --git a/README.md b/README.md index 21f657a..08a3817 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,40 @@ gh --version gh auth status ``` +## Optional GitHub Apps: fork sync + PR review + +### Pull app (Probot fork sync) + +GuardeX setup now installs a starter file at `.github/pull.yml.example`. + +To enable fork auto-sync: + +```sh +cp .github/pull.yml.example .github/pull.yml +``` + +Then edit `.github/pull.yml`: + +- set `rules[].base` to your fork branch (`main`, `master`, or `dev`) +- set `rules[].upstream` to `:` + +Install the app: +Validate config: `https://pull.git.ci/check//` + +### CR-GPT code review app + +Install app: + +`gx setup` also installs `.github/workflows/cr.yml` (GitHub Actions review workflow). + +Then in your repo: + +1. `Settings -> Secrets and variables -> Actions` +2. open `Variables` +3. add `OPENAI_API_KEY` + +After that, the app reviews new and updated pull requests automatically. + ## Companion dependency: `codex-auth` account switcher For multi-identity Codex workflows, GuardeX pairs with @@ -276,6 +310,8 @@ scripts/openspec/init-plan-workspace.sh .githooks/pre-push .codex/skills/guardex/SKILL.md .claude/commands/guardex.md +.github/pull.yml.example +.github/workflows/cr.yml .omx/state/agent-file-locks.json ``` diff --git a/bin/multiagent-safety.js b/bin/multiagent-safety.js index 9d7e6e1..d449a2e 100755 --- a/bin/multiagent-safety.js +++ b/bin/multiagent-safety.js @@ -50,7 +50,10 @@ const TEMPLATE_FILES = [ 'githooks/pre-commit', 'githooks/pre-push', 'codex/skills/guardex/SKILL.md', + 'codex/skills/guardex-merge-skills-to-dev/SKILL.md', 'claude/commands/guardex.md', + 'github/pull.yml.example', + 'github/workflows/cr.yml', ]; const EXECUTABLE_RELATIVE_PATHS = new Set([ @@ -97,6 +100,7 @@ const MANAGED_GITIGNORE_PATHS = [ '.githooks/pre-push', 'oh-my-codex/', '.codex/skills/guardex/SKILL.md', + '.codex/skills/guardex-merge-skills-to-dev/SKILL.md', '.claude/commands/guardex.md', LOCK_FILE_RELATIVE, ]; @@ -230,6 +234,25 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup GuardeX (Guardian T-R 11) Optional (GitHub remote cleanup): enable: Settings -> General -> Pull Requests -> Automatically delete head branches + +12) Optional (fork sync with Pull app): + cp .github/pull.yml.example .github/pull.yml + # then edit .github/pull.yml: + # - set rules[].base to your fork branch (main/master/dev) + # - set rules[].upstream to upstream-owner:branch + # install app: https://github.com/apps/pull + # validate config: https://pull.git.ci/check// + +13) Optional (PR review bot with cr-gpt GitHub App): + - install app: https://github.com/apps/cr-gpt + - in GitHub repo Settings -> Secrets and variables -> Actions -> Variables: + add OPENAI_API_KEY (your API key) + - the app reviews new/updated pull requests automatically + +14) Optional: test PR review action workflow + - gx setup installs .github/workflows/cr.yml + - open or update a PR + - check Actions -> "Code Review" run logs + PR timeline comments `; const AI_SETUP_COMMANDS = `npm i -g @imdeadpool/guardex @@ -249,6 +272,7 @@ openspec update gx protect add release staging gx sync --check gx sync +cp .github/pull.yml.example .github/pull.yml `; const SCORECARD_RISK_BY_CHECK = { @@ -452,6 +476,9 @@ function toDestinationPath(relativeTemplatePath) { if (relativeTemplatePath.startsWith('claude/')) { return `.${relativeTemplatePath}`; } + if (relativeTemplatePath.startsWith('github/')) { + return `.${relativeTemplatePath}`; + } throw new Error(`Unsupported template path: ${relativeTemplatePath}`); } diff --git a/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md b/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md new file mode 100644 index 0000000..7e2d13e --- /dev/null +++ b/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md @@ -0,0 +1,58 @@ +--- +name: guardex-merge-skills-to-dev +description: "Use when you need to merge SKILL.md updates from agent branches/worktrees into the local base branch (default: dev) with the multiagent-safety flow." +--- + +# GuardeX Merge Skills to dev + +Use this skill when you only want to promote Codex skill file updates into the base branch (normally `dev`) without editing the visible base checkout directly. + +## What this merges + +- `.codex/skills/**/SKILL.md` +- `templates/codex/skills/**/SKILL.md` + +## Merge runbook (safe path) + +1. Resolve the base branch: + +```sh +BASE_BRANCH="$(git config --get multiagent.baseBranch || echo dev)" +echo "$BASE_BRANCH" +``` + +2. Start a dedicated integration sandbox from base: + +```sh +bash scripts/agent-branch-start.sh "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH" +``` + +3. Enter the sandbox worktree printed by the command above. + +4. Pull only skill files from each source agent branch: + +```sh +SOURCE_BRANCH="" +git checkout "$SOURCE_BRANCH" -- ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md' +``` + +5. Verify scope before commit: + +```sh +git status --short +git diff --name-only +``` + +6. Commit and merge back to base using guardex finish flow: + +```sh +git add .codex/skills templates/codex/skills +git commit -m "Merge skill file updates into ${BASE_BRANCH}" +bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup +``` + +## Notes + +- If a source branch has non-skill changes, this runbook keeps them out of the merge. +- If merge conflicts occur, resolve only within the skill files, then rerun `agent-branch-finish.sh`. +- Do not commit directly on `dev`/`main`; always merge through an agent branch/worktree. diff --git a/templates/codex/skills/guardex/SKILL.md b/templates/codex/skills/guardex/SKILL.md index 3fc1e1f..3d21142 100644 --- a/templates/codex/skills/guardex/SKILL.md +++ b/templates/codex/skills/guardex/SKILL.md @@ -38,6 +38,7 @@ gx scan - For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "" ""`. - `scripts/codex-agent.sh` auto-syncs the sandbox branch against base before each task and auto-finishes merge/PR flow after Codex exits. - Auto-finish keeps the branch/worktree by default; remove merged branches explicitly with `gx cleanup` (or `gx cleanup --branch ""`). +- For skill-file-only merges into the local base branch (`dev` by default), use `$guardex-merge-skills-to-dev`. - Do not bypass protected branch safeguards unless explicitly required. ## Bulk merge runbook (changed agent branches) diff --git a/templates/github/pull.yml.example b/templates/github/pull.yml.example new file mode 100644 index 0000000..a8e4186 --- /dev/null +++ b/templates/github/pull.yml.example @@ -0,0 +1,6 @@ +version: "1" +rules: + - base: main + upstream: upstream-owner:main + mergeMethod: hardreset + mergeUnstable: true diff --git a/templates/github/workflows/cr.yml b/templates/github/workflows/cr.yml new file mode 100644 index 0000000..2fc2e8e --- /dev/null +++ b/templates/github/workflows/cr.yml @@ -0,0 +1,21 @@ +name: Code Review + +on: + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + +jobs: + review: + if: ${{ secrets.OPENAI_API_KEY != '' }} + runs-on: ubuntu-latest + steps: + - uses: anc95/ChatGPT-CodeReview@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENAI_API_ENDPOINT: https://api.openai.com/v1 + MODEL: gpt-4o-mini diff --git a/test/install.test.js b/test/install.test.js index dc98b70..8c65937 100644 --- a/test/install.test.js +++ b/test/install.test.js @@ -272,7 +272,10 @@ test('setup provisions workflow files and repo config', () => { '.githooks/pre-commit', '.githooks/pre-push', '.codex/skills/guardex/SKILL.md', + '.codex/skills/guardex-merge-skills-to-dev/SKILL.md', '.claude/commands/guardex.md', + '.github/pull.yml.example', + '.github/workflows/cr.yml', '.omx/state/agent-file-locks.json', '.gitignore', 'AGENTS.md', @@ -282,11 +285,11 @@ test('setup provisions workflow files and repo config', () => { assert.equal(fs.existsSync(path.join(repoDir, relativePath)), true, `${relativePath} missing`); } - const guardexSkill = fs.readFileSync(path.join(repoDir, '.codex', 'skills', 'guardex', 'SKILL.md'), 'utf8'); - assert.match(guardexSkill, /Bulk merge runbook \(changed agent branches\)/); - assert.match(guardexSkill, /gx finish --all/); - assert.match(guardexSkill, /gh pr create --base/); - assert.match(guardexSkill, /gh pr merge "" --squash --delete-branch/); + const crWorkflow = fs.readFileSync(path.join(repoDir, '.github', 'workflows', 'cr.yml'), 'utf8'); + assert.match(crWorkflow, /name:\s+Code Review/); + assert.match(crWorkflow, /pull_request:/); + assert.match(crWorkflow, /OPENAI_API_KEY/); + assert.match(crWorkflow, /anc95\/ChatGPT-CodeReview@main/); const packageJson = JSON.parse(fs.readFileSync(path.join(repoDir, 'package.json'), 'utf8')); assert.equal(packageJson.scripts['agent:codex'], 'bash ./scripts/codex-agent.sh'); @@ -318,6 +321,7 @@ test('setup provisions workflow files and repo config', () => { assert.match(gitignoreContent, /\.omx\//); assert.match(gitignoreContent, /oh-my-codex\//); assert.match(gitignoreContent, /\.codex\/skills\/guardex\/SKILL\.md/); + assert.match(gitignoreContent, /\.codex\/skills\/guardex-merge-skills-to-dev\/SKILL\.md/); assert.match(gitignoreContent, /\.claude\/commands\/guardex\.md/); assert.match(gitignoreContent, /\.omx\/state\/agent-file-locks\.json/); assert.match(gitignoreContent, /# multiagent-safety:END/); @@ -2950,6 +2954,11 @@ test('copy-prompt outputs AI setup instructions', () => { assert.match(result.stdout, /OpenSpec default change flow \(core profile\)/); assert.match(result.stdout, /\/opsx:propose /); assert.match(result.stdout, /openspec config profile /); + assert.match(result.stdout, /fork sync with Pull app/); + assert.match(result.stdout, /https:\/\/github.com\/apps\/pull/); + assert.match(result.stdout, /https:\/\/github.com\/apps\/cr-gpt/); + assert.match(result.stdout, /OPENAI_API_KEY/); + assert.match(result.stdout, /\.github\/workflows\/cr\.yml/); assert.match(result.stdout, /scripts\/agent-file-locks.py claim/); assert.match(result.stdout, /For every new user message\/task, repeat the same cycle/); }); @@ -2965,6 +2974,7 @@ test('copy-commands outputs command-only checklist', () => { assert.match(result.stdout, /scripts\/agent-file-locks.py claim/); assert.match(result.stdout, /^openspec config profile $/m); assert.match(result.stdout, /^openspec update$/m); + assert.match(result.stdout, /^cp \.github\/pull\.yml\.example \.github\/pull\.yml$/m); assert.match(result.stdout, /gx sync --check/); assert.doesNotMatch(result.stdout, /Use this exact checklist/); });