Skip to content

fix: round 8 nigeria review blockers and scoring tune #537

fix: round 8 nigeria review blockers and scoring tune

fix: round 8 nigeria review blockers and scoring tune #537

Workflow file for this run

name: validate
on:
pull_request:
branches: [main, culture/release]
jobs:
khai-encoding:
name: khai - Encoding rules
needs: [setup, cultures-stamp-check]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run encoding tests
run: python -m pytest --pyargs khai_tests.test_khai_encoding --khai-files="${{ needs.setup.outputs.files }}" -v
khai-process:
name: khai - Process rules
needs: [setup, khai-encoding]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run process tests
run: python -m pytest --pyargs khai_tests.components.test_khai_process --khai-files="${{ needs.setup.outputs.files }}" -v
khai-position:
name: khai - Position rules
needs: [setup, khai-encoding]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run position tests
run: python -m pytest --pyargs khai_tests.components.test_khai_position --khai-files="${{ needs.setup.outputs.files }}" -v
khai-piece:
name: khai - Piece rules
needs: [setup, khai-encoding]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run piece tests
run: python -m pytest --pyargs khai_tests.components.test_khai_piece --khai-files="${{ needs.setup.outputs.files }}" -v
khai-place:
name: khai - Place rules
needs: [setup, khai-encoding]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run place tests
run: python -m pytest --pyargs khai_tests.components.test_khai_place --khai-files="${{ needs.setup.outputs.files }}" -v
khai-persona:
name: khai - Persona rules
needs: [setup, khai-encoding]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run persona tests
run: python -m pytest --pyargs khai_tests.components.test_khai_persona --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-sections:
name: cultures - Section structure
needs: [setup, khai-process, khai-position, khai-piece, khai-place, khai-persona]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run section tests
run: python -m pytest tests/test_sections.py --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-branch-scope-tests:
name: cultures - Branch scope tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
# PyYAML required for E2E test (hook invokes validate_country_bag.py).
run: pip install -r tests/requirements.txt pytest
- name: Run branch_scope unit + E2E tests
run: python -m pytest tests/test_branch_scope.py tests/test_hook_scope_e2e.py -v
cultures-branch-scope-diff-check:
name: cultures - Branch scope diff check
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Check PR diff against branch scope rules
env:
BRANCH: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
run: |
set -e
git diff --name-only --diff-filter=ACMRT "origin/${BASE_REF}...HEAD" > /tmp/files.txt
if [ ! -s /tmp/files.txt ]; then
echo "No files changed; skipping scope check."
exit 0
fi
python3 - <<'PY'
import os, sys
sys.path.insert(0, "tests")
from branch_scope import classify_branch, check_scope
branch = os.environ["BRANCH"]
with open("/tmp/files.txt") as f:
files = [l.strip() for l in f if l.strip()]
kind = classify_branch(branch)
if kind == "main":
print(f"ERROR: PR head branch is main; impossible state.")
sys.exit(1)
ok, unsafe = check_scope(kind, files, branch)
if not ok:
print(f"ERROR: branch '{branch}' (classified '{kind}') has scope violations:")
for u in unsafe:
print(f" - {u}")
sys.exit(1)
print(f"OK: branch '{branch}' (classified '{kind}') passes scope check ({len(files)} file(s)).")
PY
cultures-pr-base-tests:
name: cultures - PR base tests
# Unit-test gate for tests/validate_pr_base.py (the PR base routing
# contract). Independent, always runs, no culture-file dependency.
# Catches routing regressions before pr-gate.yml enforces them in
# production. CLI file exercises the script pr-gate.yml actually calls.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Run PR base routing tests
run: python -m pytest tests/test_validate_pr_base.py tests/test_validate_pr_base_cli.py -v
cultures-audit-readme-bands-tests:
name: cultures - Audit README bands tests
# Unit-test gate for scripts/audit_readme_bands.py (the canonical Hofstede
# band contract: 0-39 Low, 40-69 Moderate, 70-100 High). Mirrors the
# cultures-branch-scope-tests pattern: independent, always runs, exercises the
# contract on every PR. Closes the gap noted during the v2 stage 2
# sequence where this test had no CI runner.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Run audit_readme_bands tests
run: python -m pytest tests/test_audit_readme_bands.py -v
cultures-hofstede-readme-updater-tests:
name: cultures - Hofstede README updater tests
# Unit-test gate for scripts/update_hofstede_readme.py (deterministic
# rewriter for the Hofstede Cultural Dimensions + Alignment Status tables
# in country READMEs). Pins the EXCELLENT/PASS/WARN/FAIL thresholds, the
# canonical band-label rendering, and the table-replacement contract.
# Mirrors the cultures-audit-readme-bands-tests pattern: independent, always runs.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
# Mirrors cultures-hofstede-derived / cultures-hofstede-bag-loader-tests: tests/requirements.txt
# has lingua-language-detector, PyYAML, and pytest. Required because
# update_hofstede_readme imports data.hofstede_keywords transitively
# via hofstede_bag_loader, and data.hofstede_keywords imports lingua
# at module level (for detect_language). The unit tests don't call
# detect_language but the import has to succeed for the module to
# load.
run: pip install -r tests/requirements.txt
- name: Run update_hofstede_readme tests
run: python -m pytest tests/test_update_hofstede_readme.py -v
cultures-history-arc:
name: cultures - History arc
# Always-run gate for the history-as-defining-moments-arc methodology
# (khai-cultures-create skill v0.1.1+). Validates every
# culture_*_history_*.md file in regions/** has:
# - *khai: piece* declaration footer
# - Yearbook section with >= 12 dated entries
# - Date range spanning >= 5 centuries (broad arc, not single event)
# Catches the class of bug PR #105 surfaced: a history file shipping
# as a narrow-event essay (the original Beeldenstorm-only Netherlands
# history) rather than a broad-arc Yearbook. Mirrors the
# cultures-audit-readme-bands-tests pattern: independent, always runs.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Run history-arc tests
run: python -m pytest tests/test_history_arc.py -v
cultures-language-preflight:
name: cultures - Language pre-flight
# Always-run pre-flight: every country README's **Language(s):** slug
# must already appear in data/language_policy.yaml. Mirrors the
# cultures-audit-readme-bands-tests / cultures-history-arc pattern: independent,
# always runs, no setup-job dependency. Fast (no lingua, no whl
# install) -- under 30 seconds.
#
# Why a separate job: the same assertion runs late in the chain via
# cultures-language, which depends on khai-language, which depends
# on the components chain (encoding -> process/position/piece/place/
# persona -> sections). When the registry is missing an entry, that
# failure surfaces 5-10 minutes into CI after every other validator
# has run. This job surfaces it in ~30 seconds on every PR so a
# missing-language case fails before the rest of the chain spins up.
#
# Incident: PR #166 (culture/spain) cleared every other validator
# then failed on this assertion because `spanish` was not in
# data/language_policy.yaml. Emergency PRs #167 (governance) and
# #168 (sync) unblocked the release at the cost of two extra round
# trips. The check itself was correct; it just ran too late to be
# useful for prevention.
#
# LANGUAGES.md section "Pre-flight: check language enablement before
# starting a culture" documents the human-side procedure that this
# gate enforces.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest + pyyaml
run: pip install pytest pyyaml
- name: Run registry pre-flight
run: python -m pytest tests/test_language.py::test_readme_language_in_registry -v
setup:
name: Setup - Compute changed files
runs-on: ubuntu-latest
outputs:
any: ${{ steps.changed.outputs.any }}
files: ${{ steps.changed.outputs.files }}
# `data_changed`: true when the PR diff touches the Hofstede reference
# dataset (data/hofstede_scores.json). Drives cultures-hofstede-reference into audit mode (run
# across all countries) so a reference update doesn't silently widen
# divergence for cultures whose declared scores are now off-band.
data_changed: ${{ steps.changed.outputs.data_changed }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed regions/*.md files
id: changed
env:
HEAD_REF: ${{ github.head_ref }}
run: |
# Sync branches (sync/<name>) are pointer branches at main's HEAD.
# Content was already validated when it landed on main. The PR diff
# against culture/release would re-surface every file that has
# advanced on main since the last sync, which is pure waste --
# short-circuit here so all downstream content jobs gated on
# `any == 'true'` skip cleanly.
if [[ "$HEAD_REF" == sync/* ]]; then
echo "Sync branch detected ($HEAD_REF); skipping content-validator file detection."
echo "any=false" >> "$GITHUB_OUTPUT"
echo "files=" >> "$GITHUB_OUTPUT"
echo "data_changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Validate fires only on pull_request, so the diff range is always
# the merge base. (The previous push trigger required handling
# `github.event.before` and HEAD~1 fallback; dropped along with
# the push trigger -- post-merge re-validation added no value.)
RANGE="origin/${{ github.base_ref }}...HEAD"
# --diff-filter=ACMRT skips deletions; the validator must not be handed paths that no longer exist.
ALL_CHANGED=$(git diff --name-only --diff-filter=ACMRT "$RANGE")
FILES=$(echo "$ALL_CHANGED" | { grep -E '^regions/.*\.md$' || true; } | tr '\n' ' ' | sed 's/ *$//')
ANY=$(if [ -z "$FILES" ]; then echo "false"; else echo "true"; fi)
DATA_CHANGED=$(if echo "$ALL_CHANGED" | grep -qx 'data/hofstede_scores.json'; then echo "true"; else echo "false"; fi)
echo "any=$ANY" >> "$GITHUB_OUTPUT"
echo "files=$FILES" >> "$GITHUB_OUTPUT"
echo "data_changed=$DATA_CHANGED" >> "$GITHUB_OUTPUT"
cultures-stamp-check:
name: cultures - Validation stamp gate
needs: [setup]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check validation stamp exists
run: |
if [ ! -f ".validation-stamp" ]; then
echo "❌ VALIDATION FAILED: .validation-stamp missing"
echo ""
echo "This file records that local validators passed before commit."
echo "Run locally to generate it:"
echo " git config core.hooksPath .githooks"
echo " scripts/setup-hooks.sh # or setup-hooks.bat on Windows"
echo " git commit --amend -m 'your message'"
exit 1
fi
echo "✔️ Validation stamp present"
- name: Check validation stamp shape
# Gate against malformed stamps (CRLF, NUL padding, BOM, wrong length,
# non-hex content). .githooks/pre-commit writes the stamp as exactly
# 40 hex chars + LF in UTF-8 (see the closing lines:
# tree_hash = run_git("write-tree")
# Path(".validation-stamp").write_text(tree_hash + "\n", encoding="utf-8")
# ). Any deviation means the stamp was written through a non-hook
# path -- typically an LLM agent's file write API (Claude Code in
# VS Code, Copilot, MCP create_or_update_file) on a Windows host
# that normalised line endings or added UTF-16 padding. The stamp
# value itself isn't checked against tree state (the hook owns
# that); this step only enforces shape so byte-exact three-way
# merges don't trip on encoding artifacts.
#
# Incident: PR #105 (culture/netherlands -> culture/release) was
# unmergeable for hours because culture/release's stamp had been
# written by Claude Code in VS Code with `\r\n\r\x00\n\x00` after
# the hash (46 bytes total, binary on disk). Both Copilot and a
# second Claude Code session tried to "just pick one" via clean
# 41-byte UTF-8 writes; git's three-way blob comparison rejected
# every attempt because the bytes never matched. Resolution
# required reverting the stamp to the merge-base blob -- a
# non-obvious path that no LLM-driven agent found on its own.
run: |
python3 - <<'PY'
import re
import sys
from pathlib import Path
raw = Path(".validation-stamp").read_bytes()
if len(raw) == 41 and re.fullmatch(rb"[0-9a-f]{40}\n", raw):
print("✔️ Validation stamp shape OK (40 hex chars + LF, 41 bytes)")
sys.exit(0)
print("❌ VALIDATION FAILED: .validation-stamp is malformed")
print(f" Expected: 41 bytes, [0-9a-f]{{40}} + LF (UTF-8, no BOM)")
print(f" Actual: {len(raw)} bytes")
print(f" Hex dump: {raw.hex()[:120]}" + ("..." if len(raw) > 60 else ""))
print()
print(" Most common cause: an LLM-driven file write (Claude Code in")
print(" VS Code, Copilot suggesting a fix, MCP create_or_update_file)")
print(" produced CRLF, UTF-16 padding, or BOM bytes instead of the")
print(" clean UTF-8 LF the pre-commit hook writes. See PR #105.")
print()
print(" Fix: re-run the pre-commit hook locally to rewrite cleanly.")
print(" touch a staged file (or git add --renormalize .)")
print(" git commit --amend --no-edit")
print(" git push --force-with-lease")
sys.exit(1)
PY
khai-language:
name: khai - Language rules
needs: [setup, cultures-sections]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests + lingua + pyyaml
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME" lingua-language-detector pyyaml
- name: Run language detection tests
run: python -m pytest --pyargs khai_tests.test_khai_language --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-language:
name: cultures - Language policy
needs: [setup, khai-language]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest + pyyaml
run: pip install pytest pyyaml
- name: Run static language checks
run: python -m pytest tests/test_language.py -v
khai-links:
name: khai - Link rules
needs: [setup, cultures-language]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run link tests
run: python -m pytest --pyargs khai_tests.test_khai_links --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-links:
name: cultures - Link integrity
needs: [setup, khai-links]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run link tests
run: python -m pytest tests/test_links.py --khai-roots=engine --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-completeness:
name: cultures - Culture completeness
needs: [setup, cultures-links]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Validate culture completeness
run: python -m pytest tests/test_completeness.py -v
cultures-audit-readme:
name: cultures - Audit README status tables
needs: [setup, cultures-completeness]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Validate audit README
run: python -m pytest tests/test_audit_readme.py -v
cultures-audit-consistency:
name: cultures - Audit table consistency
needs: [setup, cultures-audit-readme]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Validate audit consistency
run: python -m pytest tests/test_audit_consistency.py -v
khai-plagiarism:
name: khai - Phrase denylist rules
needs: [setup, cultures-audit-consistency]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install khai-tests
run: |
RELEASE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
https://api.github.com/repos/ChBrain/KAIHACKS/releases/tags/khai-tests-v0.1.7)
ASSET_INFO=$(echo "$RELEASE" | python3 -c "import sys,json; a=next(x for x in json.load(sys.stdin)['assets'] if x['name'].endswith('.whl')); print(a['url'],a['name'])")
ASSET_URL=$(echo "$ASSET_INFO" | cut -d' ' -f1)
ASSET_NAME=$(echo "$ASSET_INFO" | cut -d' ' -f2)
curl -sL \
-H "Authorization: Bearer ${{ secrets.KAIHACKS }}" \
-H "Accept: application/octet-stream" \
"$ASSET_URL" -o "/tmp/$ASSET_NAME"
pip install "/tmp/$ASSET_NAME"
- name: Run phrase denylist tests
run: python -m pytest --pyargs khai_tests.test_khai_plagiarism --khai-files="${{ needs.setup.outputs.files }}" -v
cultures-plagiarism:
name: cultures - Plagiarism structure
needs: [setup, khai-plagiarism]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Run plagiarism structural checks
run: python -m pytest tests/test_plagiarism.py -v
cultures-hofstede-structure:
name: cultures - Hofstede structure
needs: [setup, cultures-plagiarism]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Validate Hofstede structure
run: python -m pytest tests/test_hofstede_structure.py -v
cultures-hofstede-derived:
name: cultures - Hofstede derived vs declared
needs: [setup, cultures-hofstede-structure]
if: needs.setup.outputs.any == 'true'
runs-on: ubuntu-latest
env:
PR_CHANGED_FILES: ${{ needs.setup.outputs.files }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
run: pip install -r tests/requirements.txt
- name: Validate Hofstede derived scores
run: python -m pytest tests/test_hofstede_derived.py -v
cultures-hofstede-reference:
name: cultures - Hofstede declared vs reference
needs: [setup, cultures-hofstede-derived]
if: needs.setup.outputs.any == 'true' || needs.setup.outputs.data_changed == 'true'
runs-on: ubuntu-latest
env:
PR_CHANGED_FILES: ${{ needs.setup.outputs.files }}
PR_DATA_CHANGED: ${{ needs.setup.outputs.data_changed }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pytest
run: pip install pytest
- name: Run Hofstede reference tests
run: python -m pytest tests/test_hofstede_reference.py -v
cultures-hofstede-bag-loader-tests:
name: cultures - Hofstede bag loader tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
run: pip install -r tests/requirements.txt
- name: Run loader tests
run: python -m pytest tests/test_hofstede_bag_loader.py -v
cultures-hofstede-bag-integrity:
name: cultures - Hofstede bag integrity
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
run: pip install -r tests/requirements.txt
- name: Run bag integrity tests
run: |
python3 -m pytest tests/test_hofstede_bag_shape.py \
tests/test_hofstede_bag_quality.py \
tests/test_hofstede_bag_locked.py \
tests/test_hofstede_bag_completeness.py \
tests/test_hofstede_bag_fork.py \
-v
no-changes:
name: No regions/*.md files changed
needs: [setup]
if: needs.setup.outputs.any == 'false'
runs-on: ubuntu-latest
steps:
- run: echo "No regions/*.md files changed - skipping validation"