test: refactor memrl policy errors #1788
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Validate | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| doc-release-gate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run doc-release stabilization gate | |
| run: | | |
| chmod +x tests/docs/validate-doc-release.sh | |
| ./tests/docs/validate-doc-release.sh | |
| smoke-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.14' | |
| - name: Run smoke tests | |
| run: | | |
| chmod +x tests/smoke-test.sh | |
| ./tests/smoke-test.sh --verbose | |
| hook-preflight: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run hook preflight checks | |
| run: | | |
| chmod +x scripts/validate-hook-preflight.sh | |
| ./scripts/validate-hook-preflight.sh | |
| validate-hooks-doc-parity: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate hook/docs parity | |
| run: | | |
| chmod +x scripts/validate-hooks-doc-parity.sh | |
| ./scripts/validate-hooks-doc-parity.sh | |
| validate-ci-policy-parity: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate CI policy parity | |
| run: | | |
| chmod +x scripts/validate-ci-policy-parity.sh | |
| ./scripts/validate-ci-policy-parity.sh | |
| codex-runtime-sections: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate Codex runtime sections | |
| run: | | |
| chmod +x scripts/validate-codex-runtime-sections.sh | |
| ./scripts/validate-codex-runtime-sections.sh | |
| # Codex skill parity + install bundle checks removed — skills-codex/ is manually maintained. | |
| # The sync script (sync-codex-native-skills.sh) is unreliable and overwrites | |
| # manual edits. Edit skills-codex/ directly instead of regenerating. | |
| - name: Validate Codex generated artifacts | |
| run: | | |
| chmod +x scripts/validate-codex-generated-artifacts.sh | |
| ./scripts/validate-codex-generated-artifacts.sh --scope head | |
| - name: Validate Codex backbone prompts | |
| run: | | |
| chmod +x scripts/validate-codex-backbone-prompts.sh | |
| ./scripts/validate-codex-backbone-prompts.sh | |
| - name: Validate Codex override coverage | |
| run: | | |
| chmod +x scripts/validate-codex-override-coverage.sh | |
| ./scripts/validate-codex-override-coverage.sh | |
| - name: Validate Codex RPI contract | |
| run: | | |
| bash scripts/validate-codex-rpi-contract.sh | |
| - name: Validate Codex lifecycle guards | |
| run: | | |
| chmod +x scripts/validate-codex-lifecycle-guards.sh | |
| ./scripts/validate-codex-lifecycle-guards.sh | |
| - name: Validate headless runtime skills | |
| run: | | |
| chmod +x scripts/validate-headless-runtime-skills.sh | |
| ./scripts/validate-headless-runtime-skills.sh | |
| embedded-sync: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Verify embedded hooks are in sync | |
| run: | | |
| chmod +x scripts/validate-embedded-sync.sh | |
| ./scripts/validate-embedded-sync.sh | |
| cli-docs-parity: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Verify CLI docs are up to date | |
| run: | | |
| chmod +x scripts/generate-cli-reference.sh | |
| ./scripts/generate-cli-reference.sh --check | |
| shellcheck: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install ShellCheck | |
| run: sudo apt-get install -y shellcheck | |
| - name: Run ShellCheck | |
| run: | | |
| echo "=== Running ShellCheck ===" | |
| find . -name "*.sh" -type f \ | |
| -not -path "./.git/*" \ | |
| -print0 | xargs -0 -r shellcheck --severity=error || { | |
| echo "ShellCheck found errors" | |
| exit 1 | |
| } | |
| echo "✅ ShellCheck passed" | |
| markdownlint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run markdownlint | |
| uses: DavidAnson/markdownlint-cli2-action@v23 | |
| with: | |
| globs: | | |
| **/*.md | |
| security-scan: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Scan for secrets | |
| run: | | |
| echo "=== Scanning for secrets ===" | |
| # Common secret patterns | |
| patterns=( | |
| "password.*=.*['\"][^'\"]{8,}['\"]" | |
| "api[_-]?key.*=.*['\"][^'\"]{16,}['\"]" | |
| "secret.*=.*['\"][^'\"]{8,}['\"]" | |
| "(access|auth|refresh|bearer)[_-]?token.*=.*['\"][^'\"]{16,}['\"]" | |
| "AWS[_A-Z]*=.*['\"][A-Z0-9]{16,}['\"]" | |
| ) | |
| found=0 | |
| for pattern in "${patterns[@]}"; do | |
| if grep -r -i -E "$pattern" \ | |
| --exclude-dir=.git \ | |
| --exclude-dir=tests \ | |
| --exclude-dir=testdata \ | |
| --exclude-dir=cli/testdata \ | |
| --exclude-dir=dist \ | |
| --exclude-dir=.agents \ | |
| --exclude-dir=.tmp \ | |
| --exclude-dir=.gc \ | |
| --exclude="*.md" \ | |
| --exclude="*.jsonl" \ | |
| --exclude="*.sh" \ | |
| --exclude="*_test.go" \ | |
| --exclude="validate.yml" \ | |
| --binary-files=without-match \ | |
| . 2>/dev/null | grep -v 'os\.Getenv(' ; then | |
| found=1 | |
| fi | |
| done | |
| if [[ $found -eq 1 ]]; then | |
| echo "⚠️ Potential secrets found - review above" | |
| exit 1 | |
| fi | |
| echo "✅ No secrets detected" | |
| - name: Check for dangerous patterns | |
| run: | | |
| echo "=== Checking for dangerous patterns ===" | |
| # Patterns that could be dangerous in scripts | |
| # Note: validate.sh files use eval safely for CLI validation | |
| dangerous=( | |
| "rm -rf /" | |
| "curl.*\| *sh" | |
| "curl.*\| *bash" | |
| "wget.*\| *sh" | |
| ) | |
| found=0 | |
| for pattern in "${dangerous[@]}"; do | |
| if grep -r -E "$pattern" \ | |
| --include="*.sh" \ | |
| --exclude-dir=.git \ | |
| --exclude-dir=tests \ | |
| --exclude-dir=cli/testdata \ | |
| --exclude="install-opencode.sh" \ | |
| --exclude="install-codex.sh" \ | |
| --exclude="install-codex-plugin.sh" \ | |
| --exclude="install-codex-native-skills.sh" \ | |
| --exclude="ci-local-release.sh" \ | |
| . 2>/dev/null; then | |
| echo "Found: $pattern" | |
| found=1 | |
| fi | |
| done | |
| if [[ $found -eq 1 ]]; then | |
| echo "⚠️ Dangerous patterns found" | |
| exit 1 | |
| fi | |
| echo "✅ No dangerous patterns" | |
| security-toolchain-gate: | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.14' | |
| - name: Install scanner tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install semgrep ruff radon | |
| GOBIN=/usr/local/bin go install github.com/securego/gosec/v2/cmd/gosec@latest | |
| GOBIN=/usr/local/bin go install github.com/zricethezav/gitleaks/v8@latest | |
| GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4 | |
| curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin | |
| curl -sL https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 -o /tmp/hadolint && chmod +x /tmp/hadolint && sudo mv /tmp/hadolint /usr/local/bin/hadolint | |
| - name: Run security toolchain gate | |
| run: | | |
| chmod +x scripts/security-gate.sh | |
| ./scripts/security-gate.sh --mode quick | |
| env: | |
| SECURITY_GATE_OUTPUT_DIR: ${{ runner.temp }}/agentops-security | |
| TOOLCHAIN_OUTPUT_DIR: ${{ runner.temp }}/agentops-tooling | |
| - name: Upload security gate artifacts | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: security-gate | |
| path: ${{ runner.temp }}/agentops-security/ | |
| retention-days: 7 | |
| skill-integrity: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run skill structural integrity checks | |
| run: | | |
| chmod +x skills/heal-skill/scripts/heal.sh | |
| bash skills/heal-skill/scripts/heal.sh --strict | |
| skill-schema: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.14' | |
| - name: Install jsonschema | |
| run: pip install jsonschema pyyaml | |
| - name: Validate SKILL.md frontmatter against schema | |
| run: | | |
| chmod +x scripts/validate-skill-schema.sh | |
| ./scripts/validate-skill-schema.sh --verbose | |
| skill-dependency-check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate skill dependency references resolve | |
| run: | | |
| python3 - <<'PY' | |
| import re | |
| import sys | |
| from pathlib import Path | |
| skills_dir = Path("skills") | |
| skills = { | |
| p.name for p in skills_dir.iterdir() | |
| if p.is_dir() and (p / "SKILL.md").exists() | |
| } | |
| missing = [] | |
| for skill in sorted(skills): | |
| content = (skills_dir / skill / "SKILL.md").read_text(encoding="utf-8") | |
| match = re.match(r"^---\n(.*?)\n---\n", content, re.S) | |
| if not match: | |
| continue | |
| in_dependencies = False | |
| for line in match.group(1).splitlines(): | |
| stripped = line.strip() | |
| if stripped.startswith("dependencies:"): | |
| in_dependencies = True | |
| continue | |
| if in_dependencies and stripped.startswith("- "): | |
| dep = stripped[2:].split("#", 1)[0].strip().strip('"').strip("'") | |
| if dep and dep not in skills: | |
| missing.append((skill, dep)) | |
| continue | |
| if in_dependencies and stripped and not stripped.startswith("-"): | |
| in_dependencies = False | |
| if missing: | |
| print("Unresolved skill dependencies:") | |
| for skill, dep in missing: | |
| print(f" - {skill} -> {dep}") | |
| sys.exit(1) | |
| print(f"Skill dependencies resolved: {len(skills)} skills checked.") | |
| PY | |
| contract-compatibility-gate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run contract compatibility check | |
| run: | | |
| chmod +x scripts/check-contract-compatibility.sh | |
| ./scripts/check-contract-compatibility.sh | |
| - name: Run next-work contract parity check | |
| run: | | |
| chmod +x scripts/validate-next-work-contract-parity.sh | |
| ./scripts/validate-next-work-contract-parity.sh | |
| memrl-health: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check MemRL feedback loop wiring | |
| run: | | |
| chmod +x scripts/check-memrl-health.sh | |
| ./scripts/check-memrl-health.sh | |
| plugin-load-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate manifests against versioned schemas | |
| run: | | |
| chmod +x scripts/validate-manifests.sh | |
| ./scripts/validate-manifests.sh --repo-root "$GITHUB_WORKSPACE" | |
| - name: Check for broken symlinks | |
| run: | | |
| echo "=== Checking for symlinks ===" | |
| # Symlinks break when plugins installed standalone from GitHub | |
| symlinks=$(find . -type l -not -path "./.git/*" 2>/dev/null || true) | |
| if [[ -n "$symlinks" ]]; then | |
| echo "❌ Found symlinks that will break standalone installation:" | |
| echo "$symlinks" | |
| echo "" | |
| echo "Replace symlinks with actual files for standalone plugin compatibility." | |
| exit 1 | |
| fi | |
| echo "✅ No symlinks found" | |
| - name: Test plugin directory structure | |
| run: | | |
| echo "=== Testing plugin structure (unified) ===" | |
| failed=0 | |
| # Check skills/ directory at root | |
| if [[ -d "skills" ]]; then | |
| skill_count=0 | |
| for skill in skills/*/; do | |
| [[ ! -d "$skill" ]] && continue | |
| skill_name=$(basename "$skill") | |
| # Each skill must have SKILL.md | |
| if [[ ! -f "${skill}SKILL.md" ]]; then | |
| echo "❌ $skill_name: missing SKILL.md" | |
| failed=1 | |
| continue | |
| fi | |
| # SKILL.md must have YAML frontmatter with name field | |
| if ! head -1 "${skill}SKILL.md" | grep -q "^---$"; then | |
| echo "❌ $skill_name: SKILL.md missing YAML frontmatter" | |
| failed=1 | |
| continue | |
| fi | |
| if ! grep -q "^name:" "${skill}SKILL.md"; then | |
| echo "❌ $skill_name: SKILL.md missing 'name' in frontmatter" | |
| failed=1 | |
| continue | |
| fi | |
| skill_count=$((skill_count + 1)) | |
| done | |
| echo "✅ $skill_count skills valid" | |
| else | |
| echo "❌ No skills/ directory found" | |
| failed=1 | |
| fi | |
| # Check agents/ directory (optional) | |
| if [[ -d "agents" ]]; then | |
| agent_count=$(find agents -name "*.md" -type f | wc -l | tr -d ' ') | |
| echo "✅ $agent_count agents found" | |
| fi | |
| # Check hooks/ directory (optional) | |
| if [[ -f "hooks/hooks.json" ]]; then | |
| if jq empty "hooks/hooks.json" 2>/dev/null; then | |
| echo "✅ hooks.json valid" | |
| else | |
| echo "❌ hooks.json invalid JSON" | |
| failed=1 | |
| fi | |
| fi | |
| [[ $failed -eq 1 ]] && exit 1 | |
| echo "" | |
| echo "✅ Plugin structure valid" | |
| retrieval-quality: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Build ao CLI | |
| run: cd cli && go build -o /tmp/ao-test ./cmd/ao | |
| - name: Run retrieval bench (offline corpus) | |
| run: | | |
| cd cli | |
| /tmp/ao-test retrieval-bench --json | tee /tmp/retrieval-report.json | |
| PRECISION=$(python3 -c "import json; r=json.load(open('/tmp/retrieval-report.json')); print(r.get('avg_precision_at_k', r.get('avg_p_at_k', 0)))") | |
| echo "Precision@K: $PRECISION" | |
| # Warn-then-fail ratchet: advisory only in v1 (exit 0 always). | |
| # Flip to blocking once baseline stabilizes above 0.3. | |
| if python3 -c "exit(0 if float('$PRECISION') >= 0.1 else 1)" 2>/dev/null; then | |
| echo "✅ Retrieval quality above minimum threshold" | |
| else | |
| echo "⚠️ WARN: Retrieval precision below 0.1 — flywheel may be degraded" | |
| fi | |
| go-build: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 2 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Build ao CLI | |
| run: | | |
| echo "=== Building ao CLI ===" | |
| cd cli | |
| go build -o /tmp/ao-test ./cmd/ao | |
| echo "✅ ao CLI builds successfully" | |
| # Micro-epic 9 (C8): content-hash snapshot bracket. Captures the | |
| # pre-test state of ~/.agents/ and then diffs after the Go test | |
| # phase, catching any test that accidentally mutates the global | |
| # hub via a missing HOME override. Defeats the os.Chtimes mtime | |
| # bypass that the mtime-only gate could not see. Gate is | |
| # warn-only here (|| true) because CI runs on ephemeral | |
| # hub content and false positives from uninitialised hub state | |
| # should not block the pipeline until a baseline is in place. | |
| - name: Capture pre-test ~/.agents content hash snapshot | |
| run: | | |
| echo "=== Capturing agents-hub content-hash snapshot (pre-test) ===" | |
| snap="$(bash scripts/check-agents-hash-snapshot.sh capture 2>/dev/null || echo "")" | |
| if [[ -n "$snap" && -f "$snap" ]]; then | |
| echo "HASH_GATE_SNAPSHOT=$snap" >> "$GITHUB_ENV" | |
| echo "captured snapshot: $snap" | |
| else | |
| echo "snapshot capture skipped (shasum missing?)" | |
| fi | |
| - name: Run Go tests with race detection and coverage | |
| run: | | |
| echo "=== Running Go tests ===" | |
| cd cli | |
| set -o pipefail | |
| go test -race -shuffle=on -coverprofile=coverage.out -covermode=atomic ./... -v 2>&1 | tee /tmp/go-test-output.txt | |
| echo "" | |
| echo "=== Coverage Summary ===" | |
| go tool cover -func=coverage.out | tail -1 | |
| echo "✅ Go tests passed" | |
| - name: Diff post-test ~/.agents content hash snapshot | |
| if: always() | |
| run: | | |
| echo "=== Diffing agents-hub content-hash snapshot (post-test) ===" | |
| if [[ -z "${HASH_GATE_SNAPSHOT:-}" || ! -f "${HASH_GATE_SNAPSHOT:-}" ]]; then | |
| echo "::notice::hash gate inconclusive — no pre-test snapshot captured" | |
| elif bash scripts/check-agents-hash-snapshot.sh diff "$HASH_GATE_SNAPSHOT"; then | |
| echo "✅ agents-hub content-hash gate: clean" | |
| else | |
| echo "::warning::agents-hub content-hash drifted during Go test phase" | |
| echo "::warning::This means a test mutated ~/.agents/ without honoring HOME isolation." | |
| echo "::warning::Investigate which test wrote to the real hub instead of a tempdir." | |
| fi | |
| - name: Warn about slow test packages | |
| if: always() | |
| run: | | |
| echo "=== Test Package Duration Analysis ===" | |
| THRESHOLD=45 | |
| FOUND_SLOW=0 | |
| if [[ ! -f /tmp/go-test-output.txt ]]; then | |
| echo "No test output file found; skipping." | |
| exit 0 | |
| fi | |
| while IFS= read -r line; do | |
| if [[ "$line" =~ ^(ok|FAIL)[[:space:]]+([^[:space:]]+)[[:space:]]+([0-9]+(\.[0-9]+)?)s ]]; then | |
| pkg="${BASH_REMATCH[2]}" | |
| elapsed="${BASH_REMATCH[3]}" | |
| is_slow=$(awk -v e="$elapsed" -v t="$THRESHOLD" 'BEGIN { print (e > t) ? "1" : "0" }') | |
| if [[ "$is_slow" == "1" ]]; then | |
| echo "::warning::Slow test package ${pkg}: ${elapsed}s exceeds ${THRESHOLD}s threshold" | |
| FOUND_SLOW=$((FOUND_SLOW + 1)) | |
| fi | |
| fi | |
| done < /tmp/go-test-output.txt | |
| if [[ "$FOUND_SLOW" -gt 0 ]]; then | |
| echo "⚠️ Found $FOUND_SLOW slow package(s) exceeding ${THRESHOLD}s threshold" | |
| else | |
| echo "✅ All test packages completed within ${THRESHOLD}s" | |
| fi | |
| - name: Verify embedded hooks in sync | |
| run: | | |
| echo "=== Verifying embedded hooks are in sync ===" | |
| cd cli && make sync-hooks | |
| if ! git diff --exit-code -- embedded/; then | |
| echo "" | |
| echo "❌ Embedded hooks are out of sync with hooks/ source files." | |
| echo " Run 'cd cli && make sync-hooks' and commit the result." | |
| exit 1 | |
| fi | |
| echo "✅ Embedded hooks are in sync" | |
| - name: Enforce Go complexity budget on changed files | |
| run: | | |
| echo "=== Enforcing Go complexity budget ===" | |
| GOBIN=/usr/local/bin go install github.com/fzipp/gocyclo/cmd/gocyclo@latest | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" | |
| BASE_REF="origin/${{ github.base_ref }}" | |
| else | |
| BASE_REF="HEAD~1" | |
| fi | |
| ./scripts/check-go-complexity.sh --base "$BASE_REF" --warn 15 --fail 25 | |
| - name: Upload coverage to Codecov | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| files: ./cli/coverage.out | |
| flags: cli | |
| fail_ci_if_error: false | |
| - name: Upload coverage artifact | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: go-coverage | |
| path: cli/coverage.out | |
| retention-days: 7 | |
| windows-smoke: | |
| runs-on: windows-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Run native Windows smoke tests | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: .\tests\windows\test-windows-smoke.ps1 | |
| cli-integration: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Build ao CLI | |
| run: cd cli && make build | |
| - name: Run CLI commands integration test | |
| run: bash tests/integration/test-cli-commands.sh | |
| - name: Run v2.18 commands integration test | |
| run: bash tests/integration/test-v218-commands.sh | |
| - name: Run hook lifecycle test | |
| run: bash tests/hooks/test-hook-lifecycle.sh | |
| - name: Run release smoke test | |
| run: bash scripts/release-smoke-test.sh --skip-build | |
| - name: Copy embedded ao hooks for test-hooks compatibility | |
| run: cp cli/embedded/hooks/ao-*.sh hooks/ | |
| - name: Run hook integration tests | |
| run: bash tests/hooks/test-hooks.sh | |
| json-flag-consistency: | |
| runs-on: ubuntu-latest | |
| needs: go-build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Build ao CLI | |
| run: cd cli && make build | |
| - name: Run JSON flag consistency tests | |
| run: | | |
| chmod +x tests/cli/test-json-flag-consistency.sh | |
| ./tests/cli/test-json-flag-consistency.sh | |
| file-manifest-overlap: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate manifest overlap script | |
| run: | | |
| chmod +x scripts/check-file-manifest-overlap.sh | |
| bash -n scripts/check-file-manifest-overlap.sh | |
| OVERLAP_TMP=$(mktemp) | |
| CLEAN_TMP=$(mktemp) | |
| trap 'rm -f "$OVERLAP_TMP" "$CLEAN_TMP"' EXIT | |
| # Self-test: overlapping manifests should fail | |
| echo '[{"id":"1","subject":"A","files":["a.go"]},{"id":"2","subject":"B","files":["a.go"]}]' > "$OVERLAP_TMP" | |
| ! scripts/check-file-manifest-overlap.sh "$OVERLAP_TMP" | |
| # Self-test: non-overlapping should pass | |
| echo '[{"id":"1","subject":"A","files":["a.go"]},{"id":"2","subject":"B","files":["b.go"]}]' > "$CLEAN_TMP" | |
| scripts/check-file-manifest-overlap.sh "$CLEAN_TMP" | |
| doctor-check: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: go-build | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26' | |
| cache-dependency-path: cli/go.sum | |
| - name: Build ao CLI | |
| run: cd cli && go build -o /tmp/ao-test ./cmd/ao | |
| - name: Run ao doctor | |
| run: | | |
| echo "=== Running ao doctor ===" | |
| /tmp/ao-test doctor 2>/dev/null || true | |
| echo "" | |
| echo "Note: doctor may report warnings for CI-expected missing tools." | |
| echo "This job catches stale references and dead commands." | |
| skill-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install skill lint dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y jq ripgrep | |
| - name: Run skill linting | |
| run: | | |
| chmod +x tests/skills/run-all.sh | |
| bash tests/skills/run-all.sh | |
| learning-coherence: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate learning file quality | |
| run: | | |
| chmod +x scripts/validate-learning-coherence.sh | |
| bash scripts/validate-learning-coherence.sh .agents/learnings | |
| bats-tests: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install bats | |
| run: | | |
| sudo npm install -g bats | |
| - name: Run bats tests | |
| run: | | |
| echo "=== Running bats tests ===" | |
| bats tests/hooks/*.bats tests/scripts/*.bats | |
| echo "✅ Bats tests passed" | |
| - name: Run orphan hooks audit | |
| run: bash tests/hooks/test-orphan-hooks.sh | |
| check-test-staleness: | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check for stale tests | |
| run: | | |
| chmod +x scripts/check-test-staleness.sh | |
| bash scripts/check-test-staleness.sh | |
| swarm-evidence: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Validate swarm evidence files (if present) | |
| run: | | |
| chmod +x scripts/validate-swarm-evidence.sh | |
| bash scripts/validate-swarm-evidence.sh | |
| - name: Validate file manifests in evidence (if present) | |
| run: | | |
| EVIDENCE_DIR=".agents/swarm/results" | |
| if [[ ! -d "$EVIDENCE_DIR" ]]; then | |
| echo "SKIP: no evidence directory — nothing to check" | |
| exit 0 | |
| fi | |
| # Extract file manifests from any evidence that contains them | |
| MANIFEST_FILES=$(find "$EVIDENCE_DIR" -name '*.json' -type f -exec jq -e '.files_modified // empty' {} + 2>/dev/null || true) | |
| if [[ -z "$MANIFEST_FILES" ]]; then | |
| echo "SKIP: no file manifests in evidence — nothing to check" | |
| exit 0 | |
| fi | |
| echo "File manifests found in evidence — overlap check delegated to file-manifest-overlap job" | |
| summary: | |
| needs: [doc-release-gate, smoke-test, hook-preflight, validate-hooks-doc-parity, validate-ci-policy-parity, codex-runtime-sections, embedded-sync, cli-docs-parity, shellcheck, markdownlint, security-scan, security-toolchain-gate, skill-integrity, skill-schema, skill-dependency-check, contract-compatibility-gate, memrl-health, plugin-load-test, go-build, windows-smoke, cli-integration, json-flag-consistency, file-manifest-overlap, doctor-check, skill-lint, learning-coherence, bats-tests, check-test-staleness, swarm-evidence] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: Check results | |
| run: | | |
| echo "=== CI Summary ===" | |
| echo "doc-release-gate: ${{ needs.doc-release-gate.result }}" | |
| echo "smoke-test: ${{ needs.smoke-test.result }}" | |
| echo "hook-preflight: ${{ needs.hook-preflight.result }}" | |
| echo "validate-hooks-doc-parity: ${{ needs.validate-hooks-doc-parity.result }}" | |
| echo "validate-ci-policy-parity: ${{ needs.validate-ci-policy-parity.result }}" | |
| echo "codex-runtime-sections: ${{ needs.codex-runtime-sections.result }}" | |
| echo "embedded-sync: ${{ needs.embedded-sync.result }}" | |
| echo "cli-docs-parity: ${{ needs.cli-docs-parity.result }}" | |
| echo "shellcheck: ${{ needs.shellcheck.result }}" | |
| echo "markdownlint: ${{ needs.markdownlint.result }}" | |
| echo "security-scan: ${{ needs.security-scan.result }}" | |
| echo "security-toolchain-gate: ${{ needs.security-toolchain-gate.result }}" | |
| echo "skill-integrity: ${{ needs.skill-integrity.result }}" | |
| echo "skill-schema: ${{ needs.skill-schema.result }}" | |
| echo "skill-dependency-check: ${{ needs.skill-dependency-check.result }}" | |
| echo "contract-compatibility-gate: ${{ needs.contract-compatibility-gate.result }}" | |
| echo "memrl-health: ${{ needs.memrl-health.result }}" | |
| echo "plugin-load-test: ${{ needs.plugin-load-test.result }}" | |
| echo "go-build: ${{ needs.go-build.result }}" | |
| echo "windows-smoke: ${{ needs.windows-smoke.result }}" | |
| echo "cli-integration: ${{ needs.cli-integration.result }}" | |
| echo "json-flag-consistency: ${{ needs.json-flag-consistency.result }}" | |
| echo "file-manifest-overlap: ${{ needs.file-manifest-overlap.result }}" | |
| echo "doctor-check: ${{ needs.doctor-check.result }}" | |
| echo "skill-lint: ${{ needs.skill-lint.result }}" | |
| echo "learning-coherence: ${{ needs.learning-coherence.result }}" | |
| echo "bats-tests: ${{ needs.bats-tests.result }}" | |
| echo "check-test-staleness: ${{ needs.check-test-staleness.result }}" | |
| echo "swarm-evidence: ${{ needs.swarm-evidence.result }}" | |
| if [[ "${{ needs.doc-release-gate.result }}" != "success" ]] || \ | |
| [[ "${{ needs.smoke-test.result }}" != "success" ]] || \ | |
| [[ "${{ needs.hook-preflight.result }}" != "success" ]] || \ | |
| [[ "${{ needs.validate-hooks-doc-parity.result }}" != "success" ]] || \ | |
| [[ "${{ needs.validate-ci-policy-parity.result }}" != "success" ]] || \ | |
| [[ "${{ needs.codex-runtime-sections.result }}" != "success" ]] || \ | |
| [[ "${{ needs.embedded-sync.result }}" != "success" ]] || \ | |
| [[ "${{ needs.cli-docs-parity.result }}" != "success" ]] || \ | |
| [[ "${{ needs.shellcheck.result }}" != "success" ]] || \ | |
| [[ "${{ needs.markdownlint.result }}" != "success" ]] || \ | |
| [[ "${{ needs.security-scan.result }}" != "success" ]] || \ | |
| [[ "${{ needs.skill-integrity.result }}" != "success" ]] || \ | |
| [[ "${{ needs.skill-schema.result }}" != "success" ]] || \ | |
| [[ "${{ needs.skill-dependency-check.result }}" != "success" ]] || \ | |
| [[ "${{ needs.contract-compatibility-gate.result }}" != "success" ]] || \ | |
| [[ "${{ needs.memrl-health.result }}" != "success" ]] || \ | |
| [[ "${{ needs.plugin-load-test.result }}" != "success" ]] || \ | |
| [[ "${{ needs.go-build.result }}" != "success" ]] || \ | |
| [[ "${{ needs.windows-smoke.result }}" != "success" ]] || \ | |
| [[ "${{ needs.cli-integration.result }}" != "success" ]] || \ | |
| [[ "${{ needs.json-flag-consistency.result }}" != "success" ]] || \ | |
| [[ "${{ needs.file-manifest-overlap.result }}" != "success" ]] || \ | |
| [[ "${{ needs.skill-lint.result }}" != "success" ]] || \ | |
| [[ "${{ needs.learning-coherence.result }}" != "success" ]] || \ | |
| [[ "${{ needs.bats-tests.result }}" != "success" ]] || \ | |
| [[ "${{ needs.swarm-evidence.result }}" != "success" ]]; then | |
| echo "" | |
| echo "❌ Some checks failed" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✅ All checks passed" |