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
32 changes: 29 additions & 3 deletions .github/skills/resolve-merge-conflicts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,41 @@ conflicts. It:

1. Starts the merge with `--no-commit`, or resumes the current merge.
2. Refuses to auto-resolve if any conflict is not a workflow `.lock.yml`.
3. Runs `make recompile` once so generated files come from the merged Markdown.
4. Stages the regenerated conflicting lock files.
5. Verifies that no unresolved paths or whitespace errors remain.
3. Scans `.github/workflows/*.md` for leftover conflict-marker lines
(`<<<<<<<`, `|||||||`, `=======`, `>>>>>>>`) and aborts before compiling
if any are found — see "Why the marker scan matters" below.
4. Runs `make recompile` once so generated files come from the merged Markdown.
5. Stages the regenerated conflicting lock files.
6. Verifies that no unresolved paths or whitespace errors remain.

The script does not fetch, commit, push, abort, or edit workflow Markdown.
Refresh `origin/main` first only when credentials are available. After success,
review the staged merge, run the repository's final validation gate, then
commit and push.

## Why the marker scan matters

A source `.md` conflict resolved manually (by a human or an agent) can leave
a stray conflict-marker line behind — most often the rarely-noticed
`||||||| base (original)` diff3 marker — inside a workflow's YAML
frontmatter. `git diff --check` only inspects lines touched by the current
diff/staged hunks, so a marker already committed in otherwise-unchanged file
content passes silently. The gh-aw compiler then parses the marker text as a
literal YAML header option (e.g. `invalid header option: "|||||| base
(original)"`), which fails compilation later — often in an unrelated
scheduled recompilation run, far from the original merge, making the root
cause hard to trace back.

Run the standalone check any time you suspect a workflow `.md` file went
through manual conflict resolution, even outside this script's merge flow:

```bash
./.github/skills/resolve-merge-conflicts/resolve.sh --verify-markers
```

It exits non-zero and lists the offending file(s)/line(s) if any marker is
found, and does not modify the working tree.

## Safety rules

- Never choose `ours` or `theirs` for compiled lock files; regenerate them.
Expand Down
44 changes: 44 additions & 0 deletions .github/skills/resolve-merge-conflicts/resolve.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,48 @@ set -euo pipefail
usage() {
cat <<'EOF'
Usage: resolve.sh [BASE_REF]
resolve.sh --verify-markers

Merge BASE_REF (default: origin/main) without committing, then resolve
workflow lock-file-only conflicts by running make recompile.

If a merge is already in progress, BASE_REF is ignored and the current
conflicts are processed.

--verify-markers scans all .github/workflows/*.md source files (not just
files touched by the current diff) for leftover git conflict-marker lines
(<<<<<<<, |||||||, =======, >>>>>>>) and exits non-zero listing any hits.
Run it after resolving mixed conflicts and before compiling, and as a
standalone pre-flight check any time a source workflow file may have been
merged manually — `git diff --check` only inspects changed hunks and will
not catch markers already committed in unchanged file content.
EOF
}

verify_markers() {
local repo_root
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) ||
die "run this command inside a Git repository"
cd "$repo_root"

local pattern='^(<{7}|\|{7}|={7}|>{7})([^=<>|]|$)'
local hits=0
while IFS= read -r -d '' file; do
local matches
matches=$(grep -nE "$pattern" -- "$file" || true)
if [[ -n $matches ]]; then
hits=1
echo "error: leftover conflict marker(s) in $file:" >&2
echo "$matches" >&2
fi
done < <(find .github/workflows -maxdepth 1 -name '*.md' -print0 2>/dev/null)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The scan covers only maxdepth 1 under .github/workflows/, but skill YAML frontmatter in .github/skills/**/*.md could also receive manual conflict resolution and carry the same stray-marker risk. The observed failure was in a workflow .md, so the current scope is correct for the reported bug — worth documenting why the boundary is intentional.

💡 Consider a comment explaining the scope boundary

A comment next to the find call would prevent future "why don't we also scan skills?" questions:

# Scan only workflow sources — skill .md files don't feed make recompile,
# so a stray marker there is less immediately harmful.
find .github/workflows -maxdepth 1 -name '*.md' -print0 2>/dev/null

@copilot please address this.

if (( hits )); then
die "unresolved conflict markers found in workflow source; fix before compiling"
fi
echo "No conflict markers found in .github/workflows/*.md"
}

die() {
echo "error: $*" >&2
exit 1
Expand All @@ -22,6 +55,10 @@ if [[ ${1:-} == "--help" || ${1:-} == "-h" ]]; then
usage
exit 0
fi
if [[ ${1:-} == "--verify-markers" ]]; then
verify_markers
exit 0
fi
if (( $# > 1 )); then
usage >&2
exit 2
Expand Down Expand Up @@ -71,6 +108,13 @@ if (( ${#conflicts[@]} > 0 )); then
else
echo "No unresolved source conflicts; recompiling workflows..."
fi

# A manually resolved .md source conflict can silently leave behind a
# conflict-marker line (e.g. "|||||| base (original)") that git diff --check
# will not flag once staged/committed. Fail fast before compiling so the
# error surfaces here instead of during a later scheduled recompilation.
verify_markers

make recompile

if (( ${#conflicts[@]} > 0 )); then
Expand Down
Loading