let s = import "../schema.ncl" in # Mode: create-pr # Creates a GitHub/Gitea PR from the current branch. # Body is generated from CHANGELOG.md [Unreleased] section — ontoref register # must have been run before this mode to populate that section. # # Required params: # {title} — PR title (defaults to branch name if not provided) # {base} — base branch (default: main) { id = "create-pr", trigger = "Open a pull request from current branch using CHANGELOG [Unreleased] as body", preconditions = [ "gh is available in PATH and authenticated (gh auth status)", "git working tree is clean (all changes committed)", "CHANGELOG.md exists with a populated [Unreleased] section", "ontoref register was run for all changes in this branch", "Current branch is not main/master", ], steps = [ { id = "check-auth", action = "Verify gh CLI is authenticated", actor = 'Both, cmd = "gh auth status", verify = "gh auth status 2>&1 | grep -q 'Logged in'", depends_on = [], on_error = { strategy = 'Stop }, }, { id = "check-unreleased", action = "Verify CHANGELOG has content in [Unreleased] section", actor = 'Agent, cmd = "awk '/## \\[Unreleased\\]/,/## \\[/' CHANGELOG.md | grep -E '^- |^### '", verify = "awk '/## \\[Unreleased\\]/,/## \\[/' CHANGELOG.md | grep -qE '^- |^### '", depends_on = [], on_error = { strategy = 'Stop }, }, { id = "check-adr-validate", action = "Run ADR constraint validation before PR", actor = 'Both, cmd = "source scripts/reflection.sh && nu \"${DISPATCHER}\" adr validate", verify = "source scripts/reflection.sh && nu \"${DISPATCHER}\" adr validate 2>&1 | grep -qv 'FAIL'", depends_on = [], on_error = { strategy = 'Continue }, note = "Constraint failures are logged but do not block PR creation", }, { id = "extract-branch", action = "Get current branch name for PR title fallback", actor = 'Agent, cmd = "git rev-parse --abbrev-ref HEAD", verify = "git rev-parse --abbrev-ref HEAD | grep -qv 'main\\|master'", depends_on = [{ step = "check-auth", kind = 'OnSuccess }], on_error = { strategy = 'Stop }, }, { id = "build-body", action = "Extract [Unreleased] section from CHANGELOG as PR body", actor = 'Agent, cmd = "awk '/## \\[Unreleased\\]/{found=1; next} found && /^## \\[/{exit} found{print}' CHANGELOG.md | sed '/^$/N;/^\\n$/d'", verify = "awk '/## \\[Unreleased\\]/{found=1; next} found && /^## \\[/{exit} found{print}' CHANGELOG.md | grep -qE '\\S'", depends_on = [{ step = "check-unreleased", kind = 'OnSuccess }], on_error = { strategy = 'Stop }, }, { id = "create-pr", action = "Open pull request via gh CLI", actor = 'Both, cmd = "gh pr create --base {base} --title \"{title}\" --body \"$(awk '/## \\[Unreleased\\]/{found=1; next} found && /^## \\[/{exit} found{print}' CHANGELOG.md)\"", verify = "gh pr view --json number 2>/dev/null | jq '.number > 0'", depends_on = [ { step = "build-body", kind = 'OnSuccess }, { step = "extract-branch", kind = 'OnSuccess }, { step = "check-adr-validate", kind = 'Always }, ], on_error = { strategy = 'Stop }, }, { id = "show-pr", action = "Display PR URL", actor = 'Both, cmd = "gh pr view --json url | jq -r '.url'", verify = "gh pr view --json url 2>/dev/null | jq -r '.url' | grep -qE '^http'", depends_on = [{ step = "create-pr", kind = 'OnSuccess }], on_error = { strategy = 'Continue }, }, ], postconditions = [ "PR is open and visible at the URL shown", "PR body matches CHANGELOG [Unreleased] content", "ADR constraints were validated before submission", ], } | s.Mode std.string.NonEmpty