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 check:he-math-duration-projectionnpm run quality:source-coverage-audit:checknpm run check:generated-doc-noticesnpm run check:generated-status-registrynpm run check:docs-linksnpm run check:docs-indexesnpm run validate:composition-viewsnpm run quality:memory-card-review:check:allpython 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_chemistry_exam_pipeline.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.mddocs/qa-ci/curriculum-quality-dashboard.mdfor the generated Workbench quality-status viewdocs/qa-ci/curriculum-quality-maturity-and-routes.mdfor detailedM0-M4, QA-scope, route, andCQR-*semantics
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 enforces the Hessen Mathematik Sek-I G8/G9 duration projection via
check:he-math-duration-projection; the check fails if G8/G9 evidence is missing, no G8/G9 differences are detected, or any canonical-duration-grade evidence link is not represented by authored year structure or duration-specificgoalPlacements - it additionally enforces visible curriculum source coverage for the canonical Mathematik/Physik projections via
quality:source-coverage-audit:check; global missing source-backed goals may remain as non-visible rollout backlog, but visible atomic goals must have direct source/mapping evidence or explicitly accepted surrogate evidence - it additionally checks registered generated Markdown status artifacts for the standard "do not edit manually" notice via
check:generated-doc-notices - it additionally checks that the generated QA status artifact registry is in sync with
app/scripts/generatedMarkdownNoticeRegistry.tsviacheck:generated-status-registry - it additionally checks local Markdown links under
docs/viacheck:docs-links - it additionally checks coverage for the main documentation indexes via
check:docs-indexes - it additionally validates explicit learner-facing composition-view files via
validate:composition-views - it additionally enforces all configured memory-card review ledgers via
quality:memory-card-review:check:all; in the dashboard, missingCQR-302review configuration blocksM6, whileM5remains the core curriculum QA level - 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 validates the Hessen 2026 chemistry exam pipeline artifacts (
slot_matrix.json,coverage_requirements.json,task_bank.json, and source-landscape release anchors) - 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-201warning cases are tracked indocs/qa-ci/applicability-accepted-warnings.jsonand are reported as accepted warnings, not active warnings;APV-202is reported as a diagnostic finding, not as warning debt
Optional local status snapshot:
cd app
npm run quality:curriculum-status
This writes docs/qa-ci/status/curriculum-quality-status.json and .md, which are read by the local Workbench route /quality-dashboard.
Optional MEM/FWU endpoint review snapshot:
cd app
npm run quality:mem-sparql-consistency
This writes the MEM audit and review-queue artifacts under docs/qa-ci/status/. It is not part of the blocking CI workflow because the MEM endpoint is external and currently exposes concrete curriculum data only for some configured jurisdictions. See docs/qa-ci/mem-sparql-consistency-runbook.md for the operational workflow.
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,check:he-math-duration-projection,quality:source-coverage-audit:check,check:generated-doc-notices,check:generated-status-registry,check:docs-links,check:docs-indexes,validate:composition-views,quality:memory-card-review:check:all,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_chemistry_exam_pipeline.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)