Continuous Integration (CI)
This document describes the CI workflow defined in .github/workflows/ci.yml.
Triggers
The workflow runs on:
- push to
main - pull requests targeting
main
The workflow ignores documentation-only and temporary changes (README.md, AGENTS.md, docs/**, sandbox/**, tmp/**).
Jobs
1. frontend-ci
Purpose: validate frontend source quality and buildability (app/).
Steps:
npm cinpm run lintnpm run build
2. graph-validation
Purpose: validate curriculum graph/data integrity and schema constraints.
Steps:
npm ci(inapp/)npm run validate:graphnpm run validate:view-filtersnpm run validate:composition-viewspython scripts/validate_schemas.pypython scripts/validate_goal_ids_uuid.pypython scripts/validate_hessen_upper_secondary_archive_paths.pypython scripts/validate_hessen_upper_secondary_legacy_refs.pypython scripts/validate_hessen_lower_secondary_archive_paths.pypython scripts/validate_hessen_lower_secondary_legacy_refs.pypython scripts/validate_bavaria_gymnasium_archive_paths.pypython scripts/validate_bavaria_gymnasium_legacy_refs.py
The graph rule catalog is documented in:
docs/qa-ci/graph-validation-rules.md
validate:graph supports two enforcement profiles:
- default:
GVR-*rules are strict (failing) - legacy-warn mode:
VALIDATE_GRAPH_STRICT_RULES=0 npm run validate:graph
Current scope note:
- this job validates the full authored landscapes as committed
- it additionally validates projected filtered learner graphs via
validate:view-filters - it additionally validates explicit learner-facing composition-view files via
validate:composition-views - it additionally enforces the Hessen Oberstufe retained-asset boundary: under
curricula/DE/Gymnasium/input/DE-HE/abi, legacyGymnasiale_Oberstufepath strings may only remain inside allowlisted raw archival provenance files fromcurricula/DE/Gymnasium/input/DE-HE/retained-asset-registry.json - it also enforces the Hessen Oberstufe repo-handoff boundary: the retired legacy tree must stay absent from the active repo, and active tooling/runtime/test surfaces may mention it only from the explicit allowlist in
curricula/DE/Gymnasium/provenance/hessen-upper-secondary-retirement-registry.json - it now also enforces the Hessen Sek-I retained-asset boundary: under
curricula/DE/Gymnasium/input/DE-HE/lower-secondary, legacyGymnasium_9_Mittelstufepath strings are forbidden in retained operational archive content - it also enforces the Hessen Sek-I repo-handoff boundary: active tooling/runtime/test surfaces may mention the lower-secondary legacy tree only from the explicit allowlist in
curricula/DE/Gymnasium/provenance/hessen-lower-secondary-retirement-registry.json - the current CI scope for
validate:view-filtersis the reviewed canonical DE Gymnasium set (Mathematik,Physik,Chemie,Biologie,Informatik,Deutsch,Englisch,Französisch,Griechisch,Chinesisch,Geschichte,Politik und Wirtschaft,Musik,Latein,Spanisch,Wirtschaft,Overview) - the validator can be widened locally via
APPLICABILITY_VALIDATION_SCOPE=all npm run validate:view-filters, but CI does not enforce that broader scope yet - reviewed residual
APV-201/APV-202cases are tracked indocs/qa-ci/applicability-accepted-warnings.jsonand are reported as accepted warnings, not active warnings
3. backend-ci
Purpose: validate backend (backend/) via Gradle checks.
Steps:
./gradlew check
Status checks
All CI jobs must pass for a successful workflow run.
Run CI locally
From repository root:
bash run_ci.sh
This runs:
- app checks (
validate:graph,validate:view-filters,validate:composition-views,lint,build) - repo-level data checks (
validate_schemas.py,validate_goal_ids_uuid.py,validate_hessen_upper_secondary_archive_paths.py,validate_hessen_upper_secondary_legacy_refs.py,validate_hessen_lower_secondary_archive_paths.py,validate_hessen_lower_secondary_legacy_refs.py,validate_bavaria_gymnasium_archive_paths.py,validate_bavaria_gymnasium_legacy_refs.py) - backend checks (
./gradlew clean check --no-daemon)