diff --git a/.github/workflows/tutor-quality.yml b/.github/workflows/tutor-quality.yml new file mode 100644 index 0000000..943a51a --- /dev/null +++ b/.github/workflows/tutor-quality.yml @@ -0,0 +1,53 @@ +name: Tutor Course Content Quality + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + deterministic-tutor-quality: + name: Deterministic Tutor quality hard gate + runs-on: ubuntu-latest + steps: + - name: Check out Course Content + uses: actions/checkout@v4 + with: + path: course-content + + - name: Read compatible UnoSim commit + id: unosim-version + shell: bash + run: | + ref="$(tr -d '[:space:]' < course-content/.unosim-compatible-commit)" + if [[ ! "$ref" =~ ^[0-9a-f]{40}$ ]]; then + echo "Invalid .unosim-compatible-commit" >&2 + exit 1 + fi + echo "ref=$ref" >> "$GITHUB_OUTPUT" + + - name: Check out compatible UnoSim validator + uses: actions/checkout@v4 + with: + repository: MoDevIO/UnoSim + ref: ${{ steps.unosim-version.outputs.ref }} + path: unosim + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: unosim/.nvmrc + cache: npm + cache-dependency-path: unosim/package-lock.json + + - name: Install validator dependencies + working-directory: unosim + run: npm ci + + - name: Validate Course Content + working-directory: unosim + run: npm run validate:tutor-course-content -- "$GITHUB_WORKSPACE/course-content" diff --git a/.unosim-compatible-commit b/.unosim-compatible-commit new file mode 100644 index 0000000..e3f3638 --- /dev/null +++ b/.unosim-compatible-commit @@ -0,0 +1 @@ +aaa52fad3d778d4ec7ab9e304a27fe9626efb270 diff --git a/README.md b/README.md index 552c575..de12972 100644 --- a/README.md +++ b/README.md @@ -64,3 +64,19 @@ learningObjectives: ``` Tutor repository data is normalized didactic data, never a system prompt. + +### Deterministic Tutor quality gate + +`tutor/quality-cases.yaml` contains small positive and negative activation +cases that reference Examples by ID. Every Topic needs both kinds of coverage. +The gate also checks the complete pinned bundle, Topic/Question reachability, +mastery capacity, and a strict LEARN-to-DEEPEN path. It deliberately does not +try to judge semantic teaching quality. + +`.unosim-compatible-commit` pins the exact UnoSim validator used by CI; it must +be updated deliberately when the Course Content adopts a newer validator +contract. With that UnoSim revision checked out locally, run: + +```sh +npm run validate:tutor-course-content -- /path/to/UnoSim-Examples +``` diff --git a/tutor/manifest.yaml b/tutor/manifest.yaml index 1313bc6..9dc0dcc 100644 --- a/tutor/manifest.yaml +++ b/tutor/manifest.yaml @@ -3,10 +3,10 @@ defaultStrategy: precision-policy topics: - id: variables-and-serial path: tutor/topics/variables-and-serial.yaml - sha256: 84fc5f3de8722fc4c5d1b47da7bc2e262c82fd20b41d33f14aa6a244b15ca1d2 + sha256: f80f62a304e4183f4102672f0d9af4e1f8dbe5823a09bfabeba999a65f8b5c6b - id: long-values path: tutor/topics/long-values.yaml - sha256: 1cc3c7ab861443503f1f47acb2863157ae92a2489cbaa55a2b30516c89b96859 + sha256: d0c801fca7749d6b91fd743a386077ad41fd5bef002df6e6330eaff5c713b4f5 strategies: - id: precision-policy path: tutor/strategies/precision-policy.yaml diff --git a/tutor/quality-cases.yaml b/tutor/quality-cases.yaml new file mode 100644 index 0000000..98ce857 --- /dev/null +++ b/tutor/quality-cases.yaml @@ -0,0 +1,12 @@ +schemaVersion: 1 +cases: + - id: variables-serial-positive + example: it01-01-variable-speichern + expectedTopics: [variables-and-serial] + forbiddenTopics: [long-values] + - id: pwm-does-not-activate-variables + example: it04-03-pwm-ausgang + forbiddenTopics: [variables-and-serial, long-values] + - id: long-values-positive + example: it03-05-millis + expectedTopics: [long-values] diff --git a/tutor/topics/long-values.yaml b/tutor/topics/long-values.yaml index 7db6eee..d723176 100644 --- a/tutor/topics/long-values.yaml +++ b/tutor/topics/long-values.yaml @@ -64,6 +64,24 @@ questions: - fact: type-used values: [long] text: Warum passt der Datentyp long zur im Sketch gespeicherten Information? + - id: long-value-prediction + concept: execution-structure + indicator: identifies-long-value + kind: prediction + difficulty: [20, 80] + requires: + - fact: type-used + values: [long] + text: Wie erwartest du, dass sich der gespeicherte long-Wert beim nächsten Schleifendurchlauf verändert? + - id: long-value-transfer + concept: timing-interval + indicator: explains-long-usage + kind: transfer + difficulty: [20, 80] + requires: + - fact: type-used + values: [long] + text: Welche weitere zeitbezogene Information könntest du im aktuellen Sketch mit einem long-Wert erfassen, ohne den vollständigen Code zu entwerfen? scaffolds: - id: long-value-scaffold forConcept: timing-interval diff --git a/tutor/topics/variables-and-serial.yaml b/tutor/topics/variables-and-serial.yaml index 640901a..d3487e8 100644 --- a/tutor/topics/variables-and-serial.yaml +++ b/tutor/topics/variables-and-serial.yaml @@ -4,8 +4,6 @@ title: Variablen, Literale und Serial-Ausgabe locale: de-DE activation: any: - - fact: type-used - values: [int] - fact: serial-call values: [print] concepts: @@ -56,7 +54,7 @@ questions: requires: - fact: type-used values: [int] - text: Welchen Wert verwendet der Sketch an der betrachteten Integer-Variablen? + text: Welche Rolle spielt der Integer-Datentyp im aktuellen Sketch? - id: serial-output-concept concept: serial-output indicator: explains-serial-output @@ -66,6 +64,26 @@ questions: - fact: serial-call values: [print] text: Welche Rolle hat der Serial-Ausgabeaufruf in diesem Sketch? + - id: variable-output-application + concept: variable-values + indicator: relates-value-to-use + kind: application + difficulty: [20, 80] + requires: + - fact: type-used + values: [int] + - fact: serial-call + values: [print] + text: Wie hängt der gespeicherte Integerwert mit der sichtbaren seriellen Ausgabe im aktuellen Sketch zusammen? + - id: serial-output-transfer + concept: serial-output + indicator: explains-serial-output + kind: transfer + difficulty: [20, 80] + requires: + - fact: serial-call + values: [print] + text: Welche gezielte Wertänderung im aktuellen Sketch würdest du an der seriellen Ausgabe prüfen, und welche Beobachtung erwartest du? scaffolds: - id: serial-output-scaffold forConcept: serial-output