Skip to content

feat(joint): Phase 28 β€” joint intra-modal + cross-modal attribution (v1.11.0) #44

feat(joint): Phase 28 β€” joint intra-modal + cross-modal attribution (v1.11.0)

feat(joint): Phase 28 β€” joint intra-modal + cross-modal attribution (v1.11.0) #44

Workflow file for this run

name: "Konjo Quality Gate β€” Wall 2"
on:
pull_request:
branches: [main]
push:
branches: [main]
concurrency:
group: konjo-${{ github.ref }}
cancel-in-progress: true
jobs:
# ══════════════════════════════════════════════════════════════════════════
# Gate 1 β€” Static Analysis
# ══════════════════════════════════════════════════════════════════════════
static:
name: "G1 Β· Static Analysis"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install tools
run: pip install ruff mypy vulture bandit --quiet
- name: ruff lint
continue-on-error: true
run: ruff check .
- name: ruff format
continue-on-error: true
run: ruff format --check .
- name: vulture β€” dead code
continue-on-error: true
run: vulture . --min-confidence 80
- name: bandit β€” security
continue-on-error: true
run: bandit -r . -ll -q --exclude .venv,venv,tests
# ══════════════════════════════════════════════════════════════════════════
# Gate 2 β€” Tests + Coverage (β‰₯ 80%)
# ══════════════════════════════════════════════════════════════════════════
coverage:
name: "G2 Β· Tests + Coverage"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install deps
run: |
pip install pytest pytest-cov --quiet
pip install -e ".[dev]" --quiet 2>/dev/null || \
pip install -e ".[test]" --quiet 2>/dev/null || \
pip install -e . --quiet 2>/dev/null || true
- name: Run tests with coverage
continue-on-error: true
run: |
python -m pytest tests/ \
--cov=. \
--cov-report=json \
--cov-fail-under=80 \
-x -q
- name: Coverage gate
if: always()
continue-on-error: true
run: |
python3 -c "
import json, sys
try:
d = json.load(open('coverage.json'))
pct = d['totals']['percent_covered']
print(f'Line coverage: {pct:.1f}%')
if pct < 80:
print(f'::error::Coverage {pct:.1f}% < 80% gate.')
sys.exit(1)
elif pct < 95:
print(f'::warning::Coverage {pct:.1f}% < 95% target.')
except Exception as e:
print(f'Coverage data unavailable: {e}')
"
# ══════════════════════════════════════════════════════════════════════════
# Gate 3 β€” Mutation Testing (PRs only)
# ══════════════════════════════════════════════════════════════════════════
mutation:
name: "G3 Β· Mutation Testing"
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install deps
run: |
pip install mutmut pytest --quiet
pip install -e . --quiet 2>/dev/null || true
- name: Run mutation testing
continue-on-error: true
run: |
mutmut run 2>&1 | tail -60 || true
mutmut results 2>/dev/null || true
# ══════════════════════════════════════════════════════════════════════════
# Gate 4 β€” Complexity + Size + DRY
# ══════════════════════════════════════════════════════════════════════════
complexity:
name: "G4 Β· Complexity + Size + DRY"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install tools
run: pip install radon interrogate --quiet
- name: Complexity gate
continue-on-error: true
run: |
COUNT=$(radon cc . -n C --json 2>/dev/null \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
total = sum(len(v) for v in d.values())
print(total)
" 2>/dev/null || echo 0)
echo "Functions with cyclomatic complexity > 10: $COUNT"
if [ "$COUNT" -gt 0 ]; then
radon cc . -n C -s
echo "::error::$COUNT function(s) above complexity grade C."
exit 1
fi
echo "Complexity: all functions grade C or better βœ“"
- name: File size gate
continue-on-error: true
run: |
OVERSIZED=""
while IFS= read -r -d '' f; do
LC=$(wc -l < "$f")
if [ "$LC" -gt 500 ]; then
OVERSIZED="${OVERSIZED}${f}: ${LC} lines\n"
fi
done < <(find . -name "*.py" | grep -v __pycache__ | grep -v ".venv" | grep -v "venv" | tr '\n' '\0')
if [ -n "$OVERSIZED" ]; then
printf "Files exceeding 500-line limit:\n%b" "$OVERSIZED"
echo "::error::Files exceeding 500-line limit found."
exit 1
fi
echo "File sizes: all within 500-line limit βœ“"
- name: DRY check
continue-on-error: true
run: |
python3 .konjo/scripts/dry_check.py \
--threshold 0.85 \
--min-lines 20 \
--report dry_report.json 2>&1
COUNT=$(python3 -c "import json; d=json.load(open('dry_report.json')); print(d['count'])")
if [ "$COUNT" -gt 0 ]; then
echo "::error::$COUNT DRY violation(s). Abstract into shared functions."
exit 1
fi
echo "DRY check: no violations βœ“"
- name: Documentation gate
continue-on-error: true
run: interrogate . --fail-under 80 -q 2>/dev/null || true
# Gate 5 β€” Adversarial Review β€” DISABLED in CI
# Run locally: git diff HEAD~1 | python3 .konjo/scripts/konjo_review.py
# ══════════════════════════════════════════════════════════════════════════
# Final gate β€” G1 + G2 + G4 must pass
# ══════════════════════════════════════════════════════════════════════════
konjo-gate:
name: "Konjo Gate β€” All Walls Clear"
runs-on: ubuntu-latest
if: always()
needs: [static, coverage, complexity]
steps:
- name: Evaluate all required gates
run: |
STATIC="${{ needs.static.result }}"
COVERAGE="${{ needs.coverage.result }}"
COMPLEXITY="${{ needs.complexity.result }}"
echo "static: $STATIC"
echo "coverage: $COVERAGE"
echo "complexity: $COMPLEXITY"
FAILED=""
[ "$STATIC" != "success" ] && FAILED="$FAILED static"
[ "$COVERAGE" != "success" ] && FAILED="$FAILED coverage"
[ "$COMPLEXITY" != "success" ] && FAILED="$FAILED complexity"
if [ -n "$FAILED" ]; then
echo "::error::Gate(s) failed:$FAILED β€” merge blocked."
exit 1
fi
echo "All required Konjo quality gates passed βœ“"
echo "The code is seaworthy."