Skip to content

Fix #18: workflow-gate and emit-shape regressions in v1.2.0 #45

Fix #18: workflow-gate and emit-shape regressions in v1.2.0

Fix #18: workflow-gate and emit-shape regressions in v1.2.0 #45

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Validate skill frontmatter
run: |
status=0
for skill_dir in skills/*/; do
# Skip shared directory
if [ "$(basename "$skill_dir")" = "shared" ]; then
continue
fi
skill_file="${skill_dir}SKILL.md"
if [ ! -f "$skill_file" ]; then
echo "ERROR: Missing SKILL.md in $skill_dir"
status=1
continue
fi
# Check for YAML frontmatter delimiters
first_line=$(head -1 "$skill_file")
if [ "$first_line" != "---" ]; then
echo "ERROR: $skill_file missing YAML frontmatter (no opening ---)"
status=1
continue
fi
# Extract frontmatter (between first and second ---)
frontmatter=$(sed -n '2,/^---$/p' "$skill_file" | sed '$d')
# Check for name field (must be lowercase with hyphens)
if ! echo "$frontmatter" | grep -qE '^name: [a-z0-9-]+$'; then
echo "ERROR: $skill_file 'name' missing or not lowercase-with-hyphens"
status=1
fi
# Check for description field (must start with "Use when")
if ! echo "$frontmatter" | grep -qE '^description: "?Use when'; then
echo "ERROR: $skill_file 'description' missing or does not start with 'Use when'"
status=1
fi
echo "OK: $skill_file"
done
exit $status
- name: Validate agent frontmatter
run: |
status=0
for agent_file in .github/agents/*.agent.md; do
[ -f "$agent_file" ] || continue
first_line=$(head -1 "$agent_file")
if [ "$first_line" != "---" ]; then
echo "ERROR: $agent_file missing YAML frontmatter"
status=1
continue
fi
frontmatter=$(sed -n '2,/^---$/p' "$agent_file" | sed '$d')
if ! echo "$frontmatter" | grep -qE '^description:'; then
echo "ERROR: $agent_file missing 'description' in frontmatter"
status=1
fi
if ! echo "$frontmatter" | grep -qE '^handoffs:'; then
echo "ERROR: $agent_file missing 'handoffs' in frontmatter"
status=1
fi
# Detect truncation artefacts in description: unbalanced parens,
# brackets, or backticks render as broken Markdown in Copilot UIs
# (see issue #12 — pharaoh.write-plan and pharaoh.toctree-emit).
description=$(echo "$frontmatter" | sed -n 's/^description:[[:space:]]*//p')
opens=$(echo -n "$description" | tr -cd '(' | wc -c)
closes=$(echo -n "$description" | tr -cd ')' | wc -c)
if [ "$opens" -ne "$closes" ]; then
echo "ERROR: $agent_file 'description' has unbalanced parentheses ($opens '(' vs $closes ')')"
status=1
fi
obrack=$(echo -n "$description" | tr -cd '[' | wc -c)
cbrack=$(echo -n "$description" | tr -cd ']' | wc -c)
if [ "$obrack" -ne "$cbrack" ]; then
echo "ERROR: $agent_file 'description' has unbalanced brackets ($obrack '[' vs $cbrack ']')"
status=1
fi
ticks=$(echo -n "$description" | tr -cd '`' | wc -c)
if [ $((ticks % 2)) -ne 0 ]; then
echo "ERROR: $agent_file 'description' has unbalanced backticks ($ticks total)"
status=1
fi
echo "OK: $agent_file"
done
exit $status
- name: Cross-reference skills and agents
run: |
status=0
# Check every pharaoh skill has a matching agent
for skill_dir in skills/pharaoh-*/; do
skill_name=$(basename "$skill_dir")
# Convert pharaoh-change -> pharaoh.change
agent_name=$(echo "$skill_name" | sed 's/-/./')
agent_file=".github/agents/${agent_name}.agent.md"
if [ ! -f "$agent_file" ]; then
echo "ERROR: Skill $skill_name has no matching agent at $agent_file"
status=1
else
echo "OK: $skill_name <-> $agent_file"
fi
done
# Check every pharaoh agent has a matching skill
for agent_file in .github/agents/pharaoh.*.agent.md; do
[ -f "$agent_file" ] || continue
agent_basename=$(basename "$agent_file" .agent.md)
# Convert pharaoh.change -> pharaoh-change
skill_name=$(echo "$agent_basename" | sed 's/\./-/')
skill_dir="skills/${skill_name}/"
if [ ! -d "$skill_dir" ]; then
echo "ERROR: Agent $agent_basename has no matching skill at $skill_dir"
status=1
fi
done
exit $status
- name: Check internal links
shell: bash
run: |
status=0
shopt -s globstar nullglob
for md_file in *.md docs/**/*.md; do
[ -f "$md_file" ] || continue
dir=$(dirname "$md_file")
links=$(grep -oP '\[.*?\]\(\K[^)]+' "$md_file" | grep -v '^http' | grep -v '^mailto:' | grep -v '^#' | grep -v '^?' || true)
for link in $links; do
path="${link%%#*}"
[ -z "$path" ] && continue
target="${dir}/${path}"
if [ ! -e "$target" ]; then
echo "ERROR: $md_file links to '$link' but $target does not exist"
echo "FAILED" >> /tmp/link_check_failed
fi
done
done
if [ -f /tmp/link_check_failed ]; then
exit 1
fi