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
38 changes: 12 additions & 26 deletions .github/workflows/unzip-repository.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Unpack repository archive
name: Validate repository archive

on:
push:
Expand All @@ -9,21 +9,28 @@ on:
workflow_dispatch:

permissions:
contents: write
contents: read

jobs:
unpack:
archive-validation:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Unpack archive into repository root
- name: Validate archive without mutating the checkout
shell: bash
run: |
set -euo pipefail
ZIP='agents-profiles-main.zip'
if [ ! -f "$ZIP" ]; then
echo 'No repository archive is present; archive validation is not applicable.'
exit 0
fi

unzip -t "$ZIP"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
unzip -q "$ZIP" -d "$WORK"

shopt -s dotglob nullglob
Expand All @@ -34,25 +41,4 @@ jobs:
SOURCE="$WORK"
fi

for item in "$SOURCE"/*; do
name="$(basename "$item")"
[ "$name" = '.git' ] && continue
[ "$name" = '.github' ] && continue
cp -a "$item" ./
done

rm -f "$ZIP"

- name: Commit unpacked files
shell: bash
run: |
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -A
if git diff --cached --quiet; then
echo 'No changes to commit.'
exit 0
fi
git commit -m 'Unpack repository source archive'
git push
echo "Archive validated for read-only inspection from: $SOURCE"
52 changes: 52 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Validate ForgeMesh

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
root-validation:
name: Root corpus validation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Run portable root validator
run: bash validate.sh

cli-validation:
name: Nested CLI validation
runs-on: ubuntu-latest
defaults:
run:
working-directory: agents-profiles-cli
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: agents-profiles-cli/package-lock.json

- name: Install nested CLI dependencies
run: npm ci

- name: Typecheck nested CLI
run: npm run typecheck

- name: Build nested CLI
run: npm run build
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ ForgeMesh addresses a practical coordination problem: fixed AI-agent rosters do

The implemented product surface is a workforce setup and compilation tool. It does not claim to be a live autonomous engineering runtime; the generated profiles and handoff rules are inputs for supported AI coding environments.

<!-- Validation metadata: total_agents: 340 -->

### Dependency boundary

The root repository is a canonical profile corpus plus Bash/Python validation and generation scripts; it intentionally has no root npm dependency lockfile. The only npm dependency boundary is [agents-profiles-cli](agents-profiles-cli), whose package-lock.json is authoritative for the TypeScript CLI and is validated with npm ci, typecheck and build in CI. The root validator and CLI validation do not require an API key.

## Get started

### Validate the canonical corpus
Expand Down
31 changes: 26 additions & 5 deletions validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,27 @@ echo ""
echo "--- File Counts ---"
# Count only agent files in category directories (not root docs)
agent_files=$(find "${CATEGORY_DIRS[@]}" -type f -name '*.md' 2>/dev/null | wc -l)
readme_links=$(python3 - <<'PY'
import re
from pathlib import Path

readme = Path("README.md").read_text(encoding="utf-8")
for link in sorted(set(re.findall(r"\(([^()]+\.md)\)", readme))):
print(link)
PY
)
echo "Source agent files: $agent_files"
echo "README links: $(grep -oP '\([^()]+\.md\)' README.md | sed 's/[()]//g' | sort -u | wc -l)"
echo "README links: $(printf '%s\n' "$readme_links" | sed '/^$/d' | wc -l)"
echo ""

# 2. Broken link check
echo "--- Broken Links ---"
broken=0
while read -r f; do
[ ! -f "$f" ] && echo " BROKEN: $f" && broken=$((broken + 1))
done < <(grep -oP '\([^()]+\.md\)' README.md | sed 's/[()]//g' | sort -u)
if [ -n "$readme_links" ]; then
while read -r f; do
[ ! -f "$f" ] && echo " BROKEN: $f" && broken=$((broken + 1))
done <<< "$readme_links"
fi
[ "$broken" -eq 0 ] && echo " All README links resolve ✓"
echo ""

Expand Down Expand Up @@ -142,7 +153,17 @@ echo ""

# 9. Verify README metadata matches actual count
echo "--- README Metadata ---"
readme_count=$(grep -oP 'total_agents: \K\d+' README.md)
readme_count=$(python3 - <<'PY'
import re
from pathlib import Path

readme = Path("README.md").read_text(encoding="utf-8")
match = re.search(r"total_agents:\s*(\d+)", readme)
if not match:
raise SystemExit("README metadata missing total_agents")
print(match.group(1))
PY
)
source_count=$(find "${CATEGORY_DIRS[@]}" -type f -name '*.md' 2>/dev/null | wc -l)
echo " README claims: $readme_count agents"
echo " Actual source: $source_count agents"
Expand Down
Loading