@lernapps/tooling 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +67 -2
- package/archetypes/shared/a11y.ts +38 -0
- package/archetypes/shared/hooks/pre-commit +22 -0
- package/archetypes/shared/hooks/pre-push +4 -0
- package/archetypes/shared/i18n.ts +25 -0
- package/archetypes/shared/install-hooks.ts +63 -0
- package/archetypes/shared/playwright.ts +54 -0
- package/archetypes/shared/preset.ts +81 -0
- package/archetypes/shared/storage.ts +167 -0
- package/archetypes/shared/tsconfig.json +27 -0
- package/check/validation-report.v1.schema.json +116 -0
- package/dist/a11y.mjs +30 -0
- package/dist/check/rules/accessible.mjs +18 -0
- package/dist/check/rules/app-content-guide.mjs +69 -0
- package/dist/check/rules/dependency-licence.mjs +82 -0
- package/dist/check/rules/entry-fitness-matches.mjs +22 -0
- package/dist/check/rules/entry-links-resolve.mjs +44 -0
- package/dist/check/rules/explainer-readable-without-js.mjs +66 -0
- package/dist/check/rules/links-resolve.mjs +10 -0
- package/dist/check/rules/lockfile-matches.mjs +57 -0
- package/dist/check/rules/no-request-before-click.mjs +17 -0
- package/dist/check/rules/no-tracking.mjs +17 -0
- package/dist/check/rules/own-data-only.mjs +66 -0
- package/dist/check/rules/pages-reachable.mjs +25 -0
- package/dist/check/rules/usable-at-360px.mjs +19 -0
- package/dist/check-cv4I6CCH.mjs +28 -0
- package/dist/cli.mjs +3118 -0
- package/dist/i18n.mjs +11 -0
- package/dist/lint.mjs +528 -0
- package/dist/plan-DWKayEnY.mjs +75 -0
- package/dist/playwright.mjs +47 -0
- package/dist/preset.mjs +60 -0
- package/dist/skills/lernapps-app/SKILL.md +49 -0
- package/dist/skills/lernapps-cli/SKILL.md +156 -0
- package/dist/skills/lernapps-cli/references/platform-rules.md +258 -0
- package/dist/skills/lernapps-commit/SKILL.md +39 -0
- package/dist/skills/lernapps-content-growth/SKILL.md +33 -0
- package/dist/skills/lernapps-content-questions/SKILL.md +133 -0
- package/dist/skills/lernapps-content-questions/references/question-types.md +69 -0
- package/dist/skills/lernapps-content-research/SKILL.md +50 -0
- package/dist/skills/lernapps-content-research/references/sources.md +16 -0
- package/dist/skills/lernapps-exercise-patterns/SKILL.md +74 -0
- package/dist/skills/lernapps-exercise-patterns/references/biology-geography.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/chemistry.md +16 -0
- package/dist/skills/lernapps-exercise-patterns/references/computer-science.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/figures.md +10 -0
- package/dist/skills/lernapps-exercise-patterns/references/history.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/mathematics.md +19 -0
- package/dist/skills/lernapps-exercise-patterns/references/page-structures.md +8 -0
- package/dist/skills/lernapps-exercise-patterns/references/physics.md +18 -0
- package/dist/skills/lernapps-exercise-patterns/references/success.md +28 -0
- package/dist/skills/lernapps-explainer/SKILL.md +33 -0
- package/dist/skills/lernapps-explainer/references/blocks.md +83 -0
- package/dist/skills/lernapps-explainer/references/components.md +66 -0
- package/dist/skills/lernapps-explainer/references/exercises.md +115 -0
- package/dist/skills/lernapps-explainer/references/rules.md +285 -0
- package/dist/skills/lernapps-explainer/references/runtime.md +79 -0
- package/dist/skills/lernapps-explainer/references/structure.md +24 -0
- package/dist/skills/lernapps-explainer-design/SKILL.md +57 -0
- package/dist/skills/lernapps-explainer-design/references/rules.md +75 -0
- package/dist/skills/lernapps-explainer-self-assessment/SKILL.md +93 -0
- package/dist/skills/lernapps-github/SKILL.md +102 -0
- package/dist/skills/lernapps-interactive/SKILL.md +141 -0
- package/dist/skills/lernapps-interactive/references/frame.md +30 -0
- package/dist/skills/lernapps-interactive-design/SKILL.md +58 -0
- package/dist/skills/lernapps-listing/SKILL.md +44 -0
- package/dist/skills/lernapps-listing/references/keeping-updated.md +33 -0
- package/dist/skills/lernapps-quiz/SKILL.md +100 -0
- package/dist/skills/lernapps-quiz-design/SKILL.md +48 -0
- package/dist/status-CXoQBgfU.mjs +76 -0
- package/dist/storage.mjs +113 -0
- package/generator/app/.agents/skills/app-content/SKILL.md +63 -0
- package/generator/app/.github/ISSUE_TEMPLATE/inhaltsfehler.yml +25 -0
- package/generator/app/.github/workflows/pages.yml +61 -0
- package/generator/app/LICENSE +21 -0
- package/generator/app/renovate.json +4 -0
- package/generator/architecture.ts +212 -0
- package/generator/create.ts +462 -0
- package/generator/init.ts +186 -0
- package/generator/update.ts +429 -0
- package/guidance/AGENTS.md +26 -0
- package/guidance/increment-template.md +194 -0
- package/guidance/plan-front-matter.v1.schema.json +36 -0
- package/guidance/plan-template.md +233 -0
- package/guidance/subagent-brief.md +25 -0
- package/guidance/workflow-rules.md +109 -0
- package/package.json +133 -4
- package/review/verdict.v1.schema.json +134 -0
- package/skills/archetypes/lernapps-explainer/SKILL.md +33 -0
- package/skills/archetypes/lernapps-explainer/references/blocks.md +83 -0
- package/skills/archetypes/lernapps-explainer/references/components.md +66 -0
- package/skills/archetypes/lernapps-explainer/references/exercises.md +115 -0
- package/skills/archetypes/lernapps-explainer/references/rules.md +285 -0
- package/skills/archetypes/lernapps-explainer/references/runtime.md +79 -0
- package/skills/archetypes/lernapps-explainer/references/structure.md +24 -0
- package/skills/archetypes/lernapps-explainer-design/SKILL.md +57 -0
- package/skills/archetypes/lernapps-explainer-design/references/rules.md +75 -0
- package/skills/archetypes/lernapps-explainer-self-assessment/SKILL.md +93 -0
- package/skills/archetypes/lernapps-interactive/SKILL.md +141 -0
- package/skills/archetypes/lernapps-interactive/references/frame.md +30 -0
- package/skills/archetypes/lernapps-interactive-design/SKILL.md +58 -0
- package/skills/archetypes/lernapps-quiz/SKILL.md +100 -0
- package/skills/archetypes/lernapps-quiz-design/SKILL.md +48 -0
- package/skills/content/lernapps-content-questions/SKILL.md +133 -0
- package/skills/content/lernapps-content-questions/references/question-types.md +69 -0
- package/skills/content/lernapps-content-research/SKILL.md +50 -0
- package/skills/content/lernapps-content-research/references/sources.md +16 -0
- package/skills/content/lernapps-exercise-patterns/SKILL.md +74 -0
- package/skills/content/lernapps-exercise-patterns/references/biology-geography.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/chemistry.md +16 -0
- package/skills/content/lernapps-exercise-patterns/references/computer-science.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/figures.md +10 -0
- package/skills/content/lernapps-exercise-patterns/references/history.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/mathematics.md +19 -0
- package/skills/content/lernapps-exercise-patterns/references/page-structures.md +8 -0
- package/skills/content/lernapps-exercise-patterns/references/physics.md +18 -0
- package/skills/content/lernapps-exercise-patterns/references/success.md +28 -0
- package/skills/process/lernapps-app/SKILL.md +49 -0
- package/skills/process/lernapps-commit/SKILL.md +39 -0
- package/skills/process/lernapps-content-growth/SKILL.md +33 -0
- package/skills/process/lernapps-github/SKILL.md +102 -0
- package/skills/process/lernapps-listing/SKILL.md +44 -0
- package/skills/process/lernapps-listing/references/keeping-updated.md +33 -0
- package/skills/tooling/lernapps-cli/SKILL.md +156 -0
- package/skills/tooling/lernapps-cli/references/platform-rules.md +258 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Explainer rules for planning
|
|
2
|
+
|
|
3
|
+
## `topic-has-exercises`: Every page has exercises
|
|
4
|
+
|
|
5
|
+
```rule
|
|
6
|
+
id: topic-has-exercises
|
|
7
|
+
scope: archetype:explainer
|
|
8
|
+
severity: error
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Every page has at least one exercise that practises what the page explains, with solution or feedback; best in a
|
|
12
|
+
last section "Übung".
|
|
13
|
+
- The schema requires it; start, chapter and glossary pages are generated. The review judges the fit.
|
|
14
|
+
|
|
15
|
+
## `prefer-built-in-block`: A component only where no block fits
|
|
16
|
+
|
|
17
|
+
```rule
|
|
18
|
+
id: prefer-built-in-block
|
|
19
|
+
scope: archetype:explainer
|
|
20
|
+
severity: hint
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- Built-in blocks and exercises first. A component only for what none can express; say why in the plan.
|
|
24
|
+
|
|
25
|
+
## `explainer-figure-hides-answer`: The figure does not give the answer away
|
|
26
|
+
|
|
27
|
+
```rule
|
|
28
|
+
id: explainer-figure-hides-answer
|
|
29
|
+
scope: archetype:explainer
|
|
30
|
+
severity: error
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- A task's figure shows the given values, never the answer.
|
|
34
|
+
|
|
35
|
+
## `explainer-figure-explains`: The figure shows what the text says
|
|
36
|
+
|
|
37
|
+
```rule
|
|
38
|
+
id: explainer-figure-explains
|
|
39
|
+
scope: archetype:explainer
|
|
40
|
+
severity: hint
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- Each figure shows what the text next to it explains. The caption says what to look at; `alt` says what it shows.
|
|
44
|
+
|
|
45
|
+
## `explainer-plausible-numbers`: Numbers that fit the story
|
|
46
|
+
|
|
47
|
+
```rule
|
|
48
|
+
id: explainer-plausible-numbers
|
|
49
|
+
scope: archetype:explainer
|
|
50
|
+
severity: warning
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- Ranges fit the thing: prices in whole cents, people whole, a bicycle 150–1500 €.
|
|
54
|
+
- Preconditions rule out misfits (90.91 people).
|
|
55
|
+
|
|
56
|
+
## `explainer-mistakes-diagnosed`: Typical mistakes with their message
|
|
57
|
+
|
|
58
|
+
```rule
|
|
59
|
+
id: explainer-mistakes-diagnosed
|
|
60
|
+
scope: archetype:explainer
|
|
61
|
+
severity: hint
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- Computed tasks list real mistakes. Each message says what went wrong and how to fix it ("Du hast vergessen, durch
|
|
65
|
+
100 zu teilen", not "Falsch").
|
|
66
|
+
|
|
67
|
+
## `explainer-quote-sourced`: Quotes exact, with their origin
|
|
68
|
+
|
|
69
|
+
```rule
|
|
70
|
+
id: explainer-quote-sourced
|
|
71
|
+
scope: archetype:explainer
|
|
72
|
+
severity: warning
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- Quote exactly. Name its origin (author, work, year), source and licence.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-explainer-self-assessment
|
|
3
|
+
description: Use in Commit of a lernapps.net explainer, once its content and its forms of teaching are final, to add, change or leave out its self-assessment (selfAssessment in each chapter.yaml) - which exercises it asks, how many on each of its pages, and what the build checks. Also when a build problem names selfAssessment.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Self-assessment of an explainer
|
|
8
|
+
|
|
9
|
+
## What it is
|
|
10
|
+
|
|
11
|
+
- Two kinds of page: a content page of the app (`/<chapter>/<page>/`), and a step, one page of the
|
|
12
|
+
self-assessment with a few exercises.
|
|
13
|
+
- Optional, for the learner and for the app. Before learning, the learner answers exercises of the content pages,
|
|
14
|
+
without the page around them, a few on each step.
|
|
15
|
+
- No exercise is checked alone. "Auswerten" checks all of them together. The result per content page:
|
|
16
|
+
"Kannst du wahrscheinlich schon" (all right), "Schau sie dir noch einmal an" (at least half), "Lies sie genau"
|
|
17
|
+
(less), "Nicht abgefragt" (none of its exercises asked).
|
|
18
|
+
- JavaScript only: without it, it is not offered. The last result stays on the device; a new run replaces it.
|
|
19
|
+
- The chapter is the unit: each chapter declares its own in its `chapter.yaml` (at `<chapter>/einschaetzung/`,
|
|
20
|
+
offered on the chapter's page). The app's (at `einschaetzung/`, offered on the start page) is every chapter's
|
|
21
|
+
together; you write nothing for it. In an app with one chapter, the app's is the chapter's.
|
|
22
|
+
- The learner may take the result to their KI tutor. The tutor then guides chapter by chapter: to the pages that do
|
|
23
|
+
not stick, then into a new round of the chapter's self-assessment. The build writes the tutor's page; you write
|
|
24
|
+
nothing for the tutor.
|
|
25
|
+
|
|
26
|
+
## Decide
|
|
27
|
+
|
|
28
|
+
- Default: one in every chapter.
|
|
29
|
+
- None in a chapter whose pages teach what no exercise can check (an open activity, a discussion, making something):
|
|
30
|
+
the tutor goes through it page by page. None in any chapter: write the reason under "Key decisions" and tell the
|
|
31
|
+
creator in one sentence.
|
|
32
|
+
- Leave a page out when its competency cannot be checked by one of its exercises. The result says "Nicht
|
|
33
|
+
abgefragt"; that is right for such a page.
|
|
34
|
+
|
|
35
|
+
## Choose the exercises
|
|
36
|
+
|
|
37
|
+
- Read each content page's competency under "Notes" ("Ich kann …"). No "Notes": take it from the page's title,
|
|
38
|
+
description and rule. Ask the exercises that check it best.
|
|
39
|
+
- Prefer what draws a new task in each run: `generated`, or an exercise component (a `vue-component` block whose
|
|
40
|
+
component emits an answer, by its anchor). Then `number`, `ordering`, `matching`, `multiple-choice`,
|
|
41
|
+
`single-choice`. A content page asked only by exercises that can be guessed (`true-false`, a `single-choice` or a
|
|
42
|
+
small `matching`) gives a weak result: add a `generated`, a component or a `number` one of the same page.
|
|
43
|
+
- Each must be clear without its page: no "wie im Beispiel oben", no figure that stands elsewhere on the page. A
|
|
44
|
+
question carries its figure in `material`. Not clear alone → take another exercise of the page, or leave the page
|
|
45
|
+
out.
|
|
46
|
+
- An exercise component shows its task without "Prüfen", feedback or task number. Take it only when its task is
|
|
47
|
+
clear in the component itself (the task text is in the component, not in a block around it), and when it needs
|
|
48
|
+
nothing the page has to play or show first.
|
|
49
|
+
- 2 or 3 exercises per content page asked: with one alone, the page is never "Kannst du wahrscheinlich schon". A page
|
|
50
|
+
with one fitting `generated` exercise or exercise component: list it twice, each listing draws a task of its own.
|
|
51
|
+
A question is listed once.
|
|
52
|
+
|
|
53
|
+
## Write it
|
|
54
|
+
|
|
55
|
+
- Steps: 2 to 5 exercises each, best 3; fewer or more gives a build hint. Keep the order of the course, also within a
|
|
56
|
+
content page.
|
|
57
|
+
- About 3 to 8 exercises per chapter; the app's, every chapter's together, at most about 15 to 20: about ten minutes.
|
|
58
|
+
- Each exercise by its page and id: `/<chapter>/<page>/#<exercise>`, of the chapter's own pages.
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# src/prozent/chapter.yaml
|
|
62
|
+
selfAssessment:
|
|
63
|
+
pages:
|
|
64
|
+
- exercises:
|
|
65
|
+
- /prozent/prozentwert/#prozentwert-ueben
|
|
66
|
+
- /prozent/prozentsatz/#welche-rechnung
|
|
67
|
+
- /prozent/prozentsatz/#prozentsatz-ueben
|
|
68
|
+
- exercises:
|
|
69
|
+
- /prozent/grundwert/#klammern
|
|
70
|
+
- /prozent/grundwert/#grundwert-ueben
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- In the content guide (`.agents/skills/app-content/SKILL.md`), in the procedure for a new chapter or page, add the
|
|
74
|
+
step: "1 bis 3 ihrer Übungen in `selfAssessment` aufnehmen, wie Skill `lernapps-explainer-self-assessment` sagt."
|
|
75
|
+
None in the app → the content guide says why.
|
|
76
|
+
|
|
77
|
+
## Check
|
|
78
|
+
|
|
79
|
+
- Build and `lernapps check`: one line per problem (the hint on the size of a step names its file and place), e.g.
|
|
80
|
+
`src/prozent/chapter.yaml:9: selfAssessment.pages[0].exercises[0]: /prozent/prozentwert/#prozent-raten names no exercise of src/prozent/prozentwert.yaml; …`.
|
|
81
|
+
Fix where it points.
|
|
82
|
+
- Do one run yourself in `npm run dev` at `/einschaetzung/`, and one at `/<chapter>/einschaetzung/?via=tutor`: it
|
|
83
|
+
ends in "Zurück zum Tutor". For each exercise: no "oben", "im Beispiel" or other
|
|
84
|
+
reference to its page; its figure shows with it; the task is the whole question. For each content page asked: one
|
|
85
|
+
exercise at least that cannot be guessed. Then write the commit.
|
|
86
|
+
- The end-to-end tests do three runs of each self-assessment: every answer right; none; one a tutor opened.
|
|
87
|
+
|
|
88
|
+
## Gotchas
|
|
89
|
+
|
|
90
|
+
- An exercise renamed or moved → change its reference in the same commit.
|
|
91
|
+
- `einschaetzung` is reserved: no chapter or page may have that id.
|
|
92
|
+
- Never `selfAssessment` in `src/app.yaml` (build problem): the app's comes from the chapters.
|
|
93
|
+
- Nothing about a real learner in any file (`no-learner-data-in-texts`).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-github
|
|
3
|
+
description: Use in Commit, when a lernapps.net app or increment goes on GitHub and online, or a GitHub step fails - repo and branches, Pages from gh-pages, the preview pull request and its checks, bringing the branch into main, the run of the workflow Pages, the fork for the listing pull request. Which tool first, what you may do yourself, which click you ask the creator for.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# GitHub
|
|
8
|
+
|
|
9
|
+
- Branches: `main` is published; `work` holds the first version until its publish (an increment:
|
|
10
|
+
`increment/<NN>-<short name>`); `gh-pages` is served. A push to `main` publishes.
|
|
11
|
+
- Until Commit everything is local. You do the GitHub part; the creator need not know GitHub. On request:
|
|
12
|
+
<https://lernapps.net/tooling/github/>.
|
|
13
|
+
|
|
14
|
+
## Tools, in this order
|
|
15
|
+
|
|
16
|
+
1. `gh` over REST: `gh api`, `gh run`, `gh workflow`. GraphQL may be blocked, and with it the commands that use it
|
|
17
|
+
(repo view, repo edit, pr create): that is no reason to drop `gh`; use `gh api`.
|
|
18
|
+
2. GitHub MCP tools, if present.
|
|
19
|
+
3. `curl -s https://api.github.com/repos/<owner>/<repo>/…`: public reads, no token.
|
|
20
|
+
|
|
21
|
+
## 1. Repo and Pages (first version)
|
|
22
|
+
|
|
23
|
+
1. Ask the creator in one message: the repo name (a later rename can cost your access), and, if you cannot create
|
|
24
|
+
repos or change Pages, the clicks below.
|
|
25
|
+
2. No repo on GitHub: `gh api user/repos -f name=<repo>`. Refused → the creator creates an empty public repo and
|
|
26
|
+
gives you its address.
|
|
27
|
+
3. `git remote add origin <address>`.
|
|
28
|
+
4. No `main` on the remote: `git switch --orphan main`, `git commit --allow-empty -m "chore: start"`,
|
|
29
|
+
`git push -u origin main`.
|
|
30
|
+
5. No `gh-pages`: `git switch --orphan gh-pages`, `git commit --allow-empty -m "chore: gh-pages"`,
|
|
31
|
+
`git push -u origin gh-pages`.
|
|
32
|
+
6. `git switch work`; it has its own root: `git rebase --root --onto main`.
|
|
33
|
+
7. Pages from `gh-pages`, root (`pages-from-gh-pages`):
|
|
34
|
+
`gh api -X POST repos/<owner>/<repo>/pages -f build_type=legacy -f "source[branch]=gh-pages" -f "source[path]=/"`.
|
|
35
|
+
Verify: `gh api repos/<owner>/<repo>/pages --jq '.build_type, .source.branch, .source.path'` prints `legacy`,
|
|
36
|
+
`gh-pages`, `/`. "Not Found" means off.
|
|
37
|
+
8. Default branch: `gh api repos/<owner>/<repo> --jq .default_branch` prints `main`. Else (admin):
|
|
38
|
+
`gh api -X PATCH repos/<owner>/<repo> -f default_branch=main`.
|
|
39
|
+
|
|
40
|
+
## 2. Preview and checks
|
|
41
|
+
|
|
42
|
+
1. `git push -u origin <branch>`. The pre-push hook runs the heavy checks; fix failures (skill `lernapps-cli`).
|
|
43
|
+
2. Open the pull request over REST:
|
|
44
|
+
`gh api repos/<owner>/<repo>/pulls -f base=main -f head=<branch> -f title=<"Erste Version" or the increment> -f body=Vorschau`.
|
|
45
|
+
Its comment "Vorschau der App" links the preview.
|
|
46
|
+
3. Watch the checks of the head commit:
|
|
47
|
+
`gh api repos/<owner>/<repo>/commits/<sha>/check-runs --jq '.check_runs[] | [.name, .status, .conclusion]'`.
|
|
48
|
+
A check fails → `gh run view <id> --log-failed`; fix, commit, push. Green before the checkpoint "publish".
|
|
49
|
+
|
|
50
|
+
## 3. Publish
|
|
51
|
+
|
|
52
|
+
1. Only after the creator's yes at the checkpoint "publish".
|
|
53
|
+
2. One squash commit on `main`, its message as skill `lernapps-commit` says; never a merge commit.
|
|
54
|
+
First version: `git switch main`, `git merge --squash work`, `git commit`, `git push origin main`; the pull
|
|
55
|
+
request closes with it. Increment: squash-merge its pull request
|
|
56
|
+
(`gh api -X PUT repos/<owner>/<repo>/pulls/<n>/merge -f merge_method=squash -f commit_title=… -f commit_message=…`).
|
|
57
|
+
3. Run started? `gh run list --workflow Pages --branch main --commit "$(git rev-parse HEAD)"`. None after a minute
|
|
58
|
+
→ `gh workflow run Pages --ref main`.
|
|
59
|
+
4. Watch it: `gh run watch <id>`. It fails → read the failed step, fix, push.
|
|
60
|
+
5. The job `deploy` prints "published at …" (also in the run summary). Use that address; follow redirects.
|
|
61
|
+
|
|
62
|
+
## "403 Resource not accessible by integration"
|
|
63
|
+
|
|
64
|
+
- The token may not change this setting or start this run. It is a click for the creator; put it into your next
|
|
65
|
+
question, once:
|
|
66
|
+
- Pages: Settings → Pages → Build and deployment → Source: Deploy from a branch → Branch: gh-pages, folder:
|
|
67
|
+
/ (root) → Save.
|
|
68
|
+
- Default branch: Settings → General → Default branch → ⇄ → main → Update.
|
|
69
|
+
- Run: Actions → Pages → Run workflow, or the run → Re-run all jobs.
|
|
70
|
+
- Never work around it: no dummy push, no new branch to pass a setting.
|
|
71
|
+
|
|
72
|
+
## 4. Listing pull request from a fork
|
|
73
|
+
|
|
74
|
+
- Never push a branch to lernapps/apps, even if allowed. You open the pull request from the creator's fork at the
|
|
75
|
+
checkpoint "list", without asking first: it publishes nothing.
|
|
76
|
+
- Find the fork by its owner, never by its name (it may be renamed):
|
|
77
|
+
`curl -s "https://api.github.com/repos/lernapps/apps/forks?per_page=100" | jq -r '.[] | select(.owner.login=="<owner>") | .clone_url'`.
|
|
78
|
+
- No fork, or no access to it: first add the repos your environment needs (e.g. `add_repo` with push access for the
|
|
79
|
+
fork and lernapps/apps); then `gh repo fork lernapps/apps --clone=false`, or MCP. Only when both fail: the creator's
|
|
80
|
+
click (github.com/lernapps/apps → Fork), asked for once.
|
|
81
|
+
- Push `add-<id>` to the fork; open the pull request over REST:
|
|
82
|
+
`gh api repos/lernapps/apps/pulls -f base=main -f head=<owner>:add-<id> -f title=… -f body=…`.
|
|
83
|
+
- From a fork the pull request gets the results comment, but no preview.
|
|
84
|
+
|
|
85
|
+
## Gotchas
|
|
86
|
+
|
|
87
|
+
- The creator merged the pull request themselves → no second merge: check the `Pages` run of that commit and confirm
|
|
88
|
+
the address.
|
|
89
|
+
- Never `git commit-tree` or `--no-verify` to get past a hook; an empty start commit passes the hook as it is.
|
|
90
|
+
|
|
91
|
+
## `pages-from-gh-pages`: Publish from `gh-pages`, served from its root
|
|
92
|
+
|
|
93
|
+
```rule
|
|
94
|
+
id: pages-from-gh-pages
|
|
95
|
+
scope: [archetype:explainer, archetype:interactive, archetype:quiz]
|
|
96
|
+
severity: error
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- GitHub Pages serves the branch `gh-pages` from `/`: usually `https://<owner>.github.io/<repo>/`; only repos of
|
|
100
|
+
the lernapps organisation at `https://lernapps.net/<repo>/`.
|
|
101
|
+
- Previews of pull requests go below `pr-preview/` on that branch: leave the setting.
|
|
102
|
+
- Hosted elsewhere → the entry's `url` points there.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-interactive
|
|
3
|
+
description: Use in Code of a lernapps.net interactive app whenever you write or change its core, inputs, user interface, data, playthroughs, frame or arc42 architecture, or a build, playthrough or architecture check fails. What you write, the core's contract, the playthrough format, the frame and full-screen play, the architecture, and the technical rules.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Interactive app
|
|
8
|
+
|
|
9
|
+
- Runtime: `@lernapps/app-templates` (`runtime/interactive/`); leave it untouched.
|
|
10
|
+
- `create` writes a small example app: replace it with the planned parts.
|
|
11
|
+
|
|
12
|
+
## What you write
|
|
13
|
+
|
|
14
|
+
- `src/app.yaml` (schema `interactive.v1.schema.json`): what it does, how to play, competencies, languages, routes,
|
|
15
|
+
optional background sections. Rendered as the static start page and the app page `app/`.
|
|
16
|
+
- Routes: `app/#<id>`; a seed: `app/?seed=<n>#<route>`, else random.
|
|
17
|
+
- `src/core/`, `src/ui/` (root component), `src/data/`, `src/messages/`, `src/playthroughs/`, `src/main.ts`
|
|
18
|
+
(`startInteractive(core, App)`), and the app's part of `docs/arc42/`.
|
|
19
|
+
|
|
20
|
+
## Read when
|
|
21
|
+
|
|
22
|
+
- [references/frame.md](references/frame.md): before you build a control in the heading line, a wider frame,
|
|
23
|
+
full-screen play, keys or focus.
|
|
24
|
+
|
|
25
|
+
## Gotchas
|
|
26
|
+
|
|
27
|
+
- The generated `src/core/index.ts`, `src/ui/App.vue` and playthroughs follow the contract below: read them before you
|
|
28
|
+
replace them. The types are exported by `@lernapps/app-templates/interactive`.
|
|
29
|
+
- Test a timer by ticks in a playthrough; never wait real time.
|
|
30
|
+
- The arc42 hook fails on new code without a block: `git add` the new files and describe them. Never take back
|
|
31
|
+
architecture to get past the hook.
|
|
32
|
+
- A check names an `interactive-*` rule: read its section under "Rules" below before you fix.
|
|
33
|
+
|
|
34
|
+
## Core, components, data, playthroughs
|
|
35
|
+
|
|
36
|
+
### Core
|
|
37
|
+
|
|
38
|
+
- `src/core/index.ts`: pure TypeScript, no DOM; the state is plain JSON.
|
|
39
|
+
- Exports `start(seed, route)`, `step(state, input)`, and `tick(state, dt)` if it moves on its own.
|
|
40
|
+
- The runtime owns state and clock and calls `tick(state, 20)` every 20 ms.
|
|
41
|
+
- Randomness only from the seed; never `Math.random` or the clock.
|
|
42
|
+
|
|
43
|
+
### Inputs
|
|
44
|
+
|
|
45
|
+
- `{ kind: "key", key }` (`KeyboardEvent.key`) or `{ kind: "action", name, value? }` (pointer, touch).
|
|
46
|
+
- The runtime turns keydown into key inputs; the root component emits `input` (an action or a key).
|
|
47
|
+
- Unknown kinds and ignored keys: return the same state object (Tab keeps moving the focus).
|
|
48
|
+
|
|
49
|
+
### Components
|
|
50
|
+
|
|
51
|
+
- `src/ui/`: show the `state` prop; turn actions into inputs.
|
|
52
|
+
- Vue and TypeScript. The component rules apply to them and their imports: `vue-component-no-network`,
|
|
53
|
+
`vue-component-no-html-injection`, `vue-component-imports`, `vue-component-browser-globals`.
|
|
54
|
+
- Learner texts from `src/messages/<language>.json` through `translator` (`@lernapps/tooling/i18n`), never strings
|
|
55
|
+
in code (`learner-text-german`).
|
|
56
|
+
- Announce changes with `announce` (`@lernapps/tooling/a11y`); reduced motion with `prefersReducedMotion`.
|
|
57
|
+
|
|
58
|
+
### Data
|
|
59
|
+
|
|
60
|
+
- `src/data/`, own format, imported by the core.
|
|
61
|
+
|
|
62
|
+
### Playthroughs
|
|
63
|
+
|
|
64
|
+
- `src/playthroughs/<name>.yaml`: `seed`, `route`, `steps`.
|
|
65
|
+
- A step: `keys` (short form) or `inputs`, then `ticks` (20 ms each), at least one of them; it expects a part of the
|
|
66
|
+
state, an announcement, or both. No time passes except the named ticks.
|
|
67
|
+
- The build plays each twice against the core and fails on differing plays or a wrong state. The end-to-end test
|
|
68
|
+
plays the same in the browser, desktop and 360 px.
|
|
69
|
+
- Expect only what the steps decide.
|
|
70
|
+
|
|
71
|
+
### Storage
|
|
72
|
+
|
|
73
|
+
- `createStorage` (`storage-through-wrapper`, skill `lernapps-cli`).
|
|
74
|
+
|
|
75
|
+
## Architecture (arc42)
|
|
76
|
+
|
|
77
|
+
- `docs/arc42/`, arc42 DSL (`@doctc/arc42`), English, minimal.
|
|
78
|
+
- `create` copies the generic part and the interactive extension; `update` replaces them.
|
|
79
|
+
- Write only between `<!-- app: begin … -->` and `<!-- app: end … -->`; nothing outside.
|
|
80
|
+
- Chapter 5: each part of core, UI, data as a level-3 heading in section `core`, `ui` or `data`, with `parent`,
|
|
81
|
+
`path` and a diagram of the block and its parts. Each new leaf block on a deployment node in chapter 7.
|
|
82
|
+
- Concepts, decisions (canvas or DOM), declared accessibility with reasons: in the sections at the chapter ends.
|
|
83
|
+
- 1–2 sentences per block or decision: responsibility, reason; nothing the code says.
|
|
84
|
+
- Update with every change, same commit as the code.
|
|
85
|
+
- Pre-commit `arc42 diff --staged` fails on: block changed without prose (or the reverse); new `src/` code no block
|
|
86
|
+
claims and not in `.arc42ignore`. It reads staged files: `git add` new files.
|
|
87
|
+
- Review hint (code under an interface's path changed): warning; read and update if needed.
|
|
88
|
+
- Accept a finding with `ARC42_CONSISTENT` only for a shown consistency finding judged intended, with a note in the
|
|
89
|
+
plan. Never preset it (e.g. `$(git rev-parse HEAD)`): a bypass like `--no-verify`.
|
|
90
|
+
- Unclaimed new code is never accepted: give it a block, or list config in `.arc42ignore`.
|
|
91
|
+
- `lernapps check` runs `arc42 validate --strict` on the working tree: any error, warning or hint fails.
|
|
92
|
+
- Your work; no creator checkpoint.
|
|
93
|
+
|
|
94
|
+
## Rules
|
|
95
|
+
|
|
96
|
+
### `interactive-app-valid`: The description and the playthroughs validate
|
|
97
|
+
|
|
98
|
+
```rule
|
|
99
|
+
id: interactive-app-valid
|
|
100
|
+
scope: archetype:interactive
|
|
101
|
+
severity: error
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- `src/app.yaml` and every playthrough valid against their schemas; German first, every language complete.
|
|
105
|
+
- Unique ids; at least one playthrough; each step expects a state or an announcement; playthrough routes exist.
|
|
106
|
+
- Build names each problem with file and line.
|
|
107
|
+
|
|
108
|
+
### `interactive-core-contract`: The core keeps its contract
|
|
109
|
+
|
|
110
|
+
```rule
|
|
111
|
+
id: interactive-core-contract
|
|
112
|
+
scope: archetype:interactive
|
|
113
|
+
severity: error
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `src/core/index.ts` exports `start(seed, route)`, `step(state, input)`; `tick(state, dt)` as a function if it
|
|
117
|
+
moves on its own.
|
|
118
|
+
- Every route starts; nothing throws; same seed, route, inputs, ticks → same states.
|
|
119
|
+
|
|
120
|
+
### `interactive-playthroughs-pass`: Every playthrough plays as written
|
|
121
|
+
|
|
122
|
+
```rule
|
|
123
|
+
id: interactive-playthroughs-pass
|
|
124
|
+
scope: archetype:interactive
|
|
125
|
+
severity: error
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- After each step the state has the expected fields; the browser shows the expected announcement.
|
|
129
|
+
- Failure → fix core or playthrough, whichever is wrong; never delete a playthrough.
|
|
130
|
+
|
|
131
|
+
### `interactive-architecture-current`: The architecture is valid and current
|
|
132
|
+
|
|
133
|
+
```rule
|
|
134
|
+
id: interactive-architecture-current
|
|
135
|
+
scope: archetype:interactive
|
|
136
|
+
severity: error
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- `docs/arc42/`: generic part and extension as `create`/`update` wrote them, unchanged outside the app sections.
|
|
140
|
+
- App part describes the code: every part of `src/` has its block, every block its code.
|
|
141
|
+
- `arc42 validate --strict` and `arc42 diff --staged` pass; the review checks it says what the code does.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Frame and full-screen game
|
|
2
|
+
|
|
3
|
+
## Frame
|
|
4
|
+
|
|
5
|
+
- App page: header (language switch only; none with one language), `<main>` (heading line: the title as h1 and a
|
|
6
|
+
slot beside it; `<noscript>` note, route links, `.interactive-app` mount), footer (imprint, privacy). At most
|
|
7
|
+
`--page-max-width` wide, 44rem by default.
|
|
8
|
+
- A control in the heading line: `startInteractive(core, App, { title: Controls })`. The runtime mounts it into the
|
|
9
|
+
slot, with the root component's props (`state`, `lang`) and `input` event. Never hide or rebuild the h1. Without
|
|
10
|
+
`title` the slot stays hidden.
|
|
11
|
+
- A wider frame: `--page-max-width` (e.g. `60rem`) in the app's own `:root` rule; it widens header, page and
|
|
12
|
+
footer. Never override the runtime's selectors.
|
|
13
|
+
- Start page: own static page, no script; the app page never changes it.
|
|
14
|
+
|
|
15
|
+
## Full-screen while playing
|
|
16
|
+
|
|
17
|
+
- Own overlay only: root element class from state (e.g. `state.phase === "playing"`), `position: fixed; inset: 0`,
|
|
18
|
+
page background. Leave runtime markup and CSS.
|
|
19
|
+
- Way out: visible button ("Pause"); Escape ends the overlay as a core input. Time limit or other loss → entry
|
|
20
|
+
`limits`.
|
|
21
|
+
- Browser full screen (`element.requestFullscreen()`): optional, only after a click, via template ref; overlay works
|
|
22
|
+
without it. Browser globals only in `onMounted` (`vue-component-browser-globals`).
|
|
23
|
+
|
|
24
|
+
## Keys and focus
|
|
25
|
+
|
|
26
|
+
- Runtime gives keys to the core only when focus is not on a link, button, form field.
|
|
27
|
+
- Play area focusable (`tabindex="0"`, `aria-label`); focus it via template ref (`area.value?.focus()`) when play
|
|
28
|
+
starts. Focus visible.
|
|
29
|
+
- Space and arrows: runtime prevents default only for state-changing keys. Let the core take them while playing, or
|
|
30
|
+
`@keydown.space.prevent` (and arrows) on the play area.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-interactive-design
|
|
3
|
+
description: Use in Explore right after choosing the `interactive` archetype - how the content of a lernapps.net interactive app maps onto core, user interface, data and playthroughs, which the plan then applies.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Designing an interactive app
|
|
8
|
+
|
|
9
|
+
## What an interactive app is
|
|
10
|
+
|
|
11
|
+
- The learner's doing is the content: play, train, work in one app page. A static start page without script says
|
|
12
|
+
what it is.
|
|
13
|
+
- The code is yours. The runtime renders the frame, owns state and clock, and feeds inputs to your core.
|
|
14
|
+
- Write in "Content method" one line per part below; the architecture in Code follows it.
|
|
15
|
+
|
|
16
|
+
## Can and cannot
|
|
17
|
+
|
|
18
|
+
- The README of the template says it: <https://raw.githubusercontent.com/lernapps/app-templates/main/interactive/README.md>.
|
|
19
|
+
Read it before this skill's mapping, if you have not yet.
|
|
20
|
+
|
|
21
|
+
## Parts
|
|
22
|
+
|
|
23
|
+
1. **Core:** pure TypeScript, no DOM; the state is plain JSON. It starts from a seed and a route, takes inputs,
|
|
24
|
+
and moves on with ticks if time matters. Randomness only from the seed: same seed, route and inputs → same
|
|
25
|
+
states.
|
|
26
|
+
2. **User interface:** Vue components show the state and turn actions into inputs. They keep no game state of
|
|
27
|
+
their own. Render as DOM, SVG or canvas.
|
|
28
|
+
3. **Data:** the content (levels, tasks, texts) as data files the core imports. A new level is a data change.
|
|
29
|
+
4. **Playthroughs:** one per behaviour to keep, timing included: seed, route, inputs, ticks, the state expected. They
|
|
30
|
+
are the proof that the core works; the build plays each twice, the browser once.
|
|
31
|
+
5. **Routes:** one per mode or level the entry may link to (`app/#<id>`).
|
|
32
|
+
|
|
33
|
+
## Decide
|
|
34
|
+
|
|
35
|
+
- Inputs: which keys, which buttons or touch actions. Every action works by keyboard.
|
|
36
|
+
- Timing: time limits and moving parts are ticks of the core, never the clock.
|
|
37
|
+
- Storage: small (at most 100 kB), the learning state only; no settings dialog.
|
|
38
|
+
- Libraries: bundled from `dependencies`, open licence; never a CDN.
|
|
39
|
+
- Accessibility: WCAG 2.1 AA and 360 px are the default. A deviation the idea needs (a time limit) goes into the
|
|
40
|
+
entry's `limits`, in German.
|
|
41
|
+
- Look and feel: as the creator chose in Explore.
|
|
42
|
+
- Languages: German; each declared language complete; learner texts in message files, never in code.
|
|
43
|
+
|
|
44
|
+
## Gotchas
|
|
45
|
+
|
|
46
|
+
- "Low stimulation" means no overload, not no fun: a game may look like a game (bold colours, a real board). No
|
|
47
|
+
flashing; calm motion, less with reduced motion; sound off by default. The start page stays calm.
|
|
48
|
+
- A timer is a tick of the core with a playthrough that tests the end of time, not `setTimeout` in a component.
|
|
49
|
+
|
|
50
|
+
## `start-page-explains`: The start page explains the app
|
|
51
|
+
|
|
52
|
+
```rule
|
|
53
|
+
id: start-page-explains
|
|
54
|
+
scope: archetype:interactive
|
|
55
|
+
severity: hint
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- The static start page says what learners train, for whom, and how to begin.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-listing
|
|
3
|
+
description: Use in Commit when the creator wants their app in the lernapps.net app overview, or when the listing pull request gets a results comment - even if they only say "eintragen" or "veröffentlichen". The steps to the listing pull request and how to act on its results.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Listing
|
|
8
|
+
|
|
9
|
+
- A pull request to [lernapps/apps](https://github.com/lernapps/apps) adds the app's entry.
|
|
10
|
+
- Rules: `entry-from-schema`, `entry-links-resolve`, `entry-fitness-matches` (skill `lernapps-cli`,
|
|
11
|
+
`references/platform-rules.md`).
|
|
12
|
+
|
|
13
|
+
## Open the pull request
|
|
14
|
+
|
|
15
|
+
At the checkpoint "list", without asking first: the pull request publishes nothing and is the review. After
|
|
16
|
+
"bestanden", show the creator the pull request and its result; change nothing in it without their word.
|
|
17
|
+
|
|
18
|
+
1. Follow the listing guide <https://lernapps.net/apps/enter/llms.txt> step by step: where the entry goes, how to
|
|
19
|
+
check it, how to open the pull request.
|
|
20
|
+
2. Fill the entry from the plan's entry draft and <https://lernapps.net/apps/schemas/entry.v1.schema.json>, fetched
|
|
21
|
+
now. The schema describes every field.
|
|
22
|
+
3. Run the check the guide names. Fix its findings first: the pull request runs the same check.
|
|
23
|
+
4. Open the pull request from the creator's fork as skill `lernapps-github` says.
|
|
24
|
+
|
|
25
|
+
## Results comment
|
|
26
|
+
|
|
27
|
+
- The listing guide says how to read it. Repeat until "bestanden".
|
|
28
|
+
- A fix of the app counts only once it is published (plan, Commit); then push to the pull request's branch.
|
|
29
|
+
- Then the owner of lernapps.net reviews and decides. Wait; tell the creator the comment and the decision.
|
|
30
|
+
|
|
31
|
+
## After the merge
|
|
32
|
+
|
|
33
|
+
- lernapps.net builds a copy of the app from the entry's `commit` and serves it at
|
|
34
|
+
`https://lernapps.net/apps/start/<id>/`; the catalog links it once it is checked there. Tell the creator: the app
|
|
35
|
+
stays theirs, at their own address too.
|
|
36
|
+
- Tell the creator once how to keep the app up to date: two GitHub Apps and a few settings of their repo, in
|
|
37
|
+
[references/keeping-updated.md](references/keeping-updated.md). Without them the app gets no tooling updates, and
|
|
38
|
+
lernapps/apps lists it in an issue. It is advice; the listing does not wait for it.
|
|
39
|
+
- A new version needs no new pull request from you. Publish it as usual (plan, Commit). Once the app's address
|
|
40
|
+
serves the new commit, lernapps/apps opens an update pull request for the entry within about 30 minutes; the
|
|
41
|
+
listing check runs on it.
|
|
42
|
+
- It merges itself when nothing else changed than what `lernapps update` makes, versions of npm dependencies and
|
|
43
|
+
Markdown outside `src/` and `public/`. Anything else (content, code, workflows edited by hand, the licence) waits
|
|
44
|
+
for the owner's review. Tell the creator which of the two to expect.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Keeping a listed app up to date
|
|
2
|
+
|
|
3
|
+
Tell the creator once, at the listing. It is advice; the listing does not wait for it. The creator owns the repo, so
|
|
4
|
+
these are their clicks. Offer to go through them together.
|
|
5
|
+
|
|
6
|
+
## 1. Two GitHub Apps, on the app's repo only
|
|
7
|
+
|
|
8
|
+
Each opens a page: choose the creator's account, "Only select repositories", the app's repo, Install.
|
|
9
|
+
|
|
10
|
+
- `https://github.com/apps/lernapps-updater/installations/new`: every day lernapps/apps runs `lernapps update` in the
|
|
11
|
+
app and opens a pull request that merges once the app's check is green. Without it the app gets no tooling updates,
|
|
12
|
+
and lernapps/apps lists it in an issue.
|
|
13
|
+
- `https://github.com/apps/renovate/installations/new`: third-party packages, using the app's `renovate.json`. The
|
|
14
|
+
file does nothing without the App.
|
|
15
|
+
|
|
16
|
+
## 2. Pull request settings
|
|
17
|
+
|
|
18
|
+
Settings → General → Pull Requests: select **Allow auto-merge** and **Allow squash merging** (a linear history
|
|
19
|
+
needs squash or rebase).
|
|
20
|
+
|
|
21
|
+
## 3. One ruleset for `main`
|
|
22
|
+
|
|
23
|
+
Settings → Rules → Rulesets → New ruleset → New branch ruleset. Name `main`, enforcement **Active**, Target branches
|
|
24
|
+
→ Add a target → include the default branch (pattern `main`). Rules:
|
|
25
|
+
|
|
26
|
+
- **Require a pull request before merging**: no approvals needed; as merge type squash.
|
|
27
|
+
- **Require linear history**.
|
|
28
|
+
- **Require status checks to pass before merging**: add the check `app` (the job of the workflow `Pages`). GitHub
|
|
29
|
+
offers a check only after it has run once, so the app must have had a pull request or a push first.
|
|
30
|
+
- **Block force pushes** (on by default).
|
|
31
|
+
- Bypass list → Add bypass → the repository admins, so the creator can still push by hand.
|
|
32
|
+
|
|
33
|
+
A required check the repo does not produce blocks every pull request for good: use `app`, no other name.
|