Thank you for considering a contribution. Adaptive Learner is a project skeleton template built on PluginForge. The skeleton ships with a working full-stack foundation; most non-trivial features should land as plugins, not core changes.
backend/- FastAPI app, SQLAlchemy models, Alembic migrationsfrontend/- React + TypeScript + Vite, TipTap editorplugins/- empty placeholder + plugin loader (zero plugins ship)launcher/- Cross-platform launcher (PyInstaller)docs/- Architecture overview, MkDocs site, in-app help structure.claude/rules/- Project rules read on demande2e/- Playwright smoke + full suites
New here? Start with the Developer Onboarding guide — a practical, step-by-step walkthrough of your first bug-fix (clone → find a bug → branch → fix → test → PR). The sections below are the reference details it builds on.
- Python 3.11 or newer
- Node.js 24 (Active LTS; 20.19+ also works for tests but the full Vite 8 build requires 24)
- Poetry (Python dependency management)
- Docker + Docker Compose v2+ (for the prod-shape integration
flow; not required for
make dev)
git clone https://github.com/astrapi69/adaptive_learner.git
cd adaptive_learner
make install # Poetry + npm + plugin path-deps
make test # baseline; should be green before you start
make dev # backend on :18001, frontend on :15174 (overridable via ADAPTIVE_LEARNER_PORT / ADAPTIVE_LEARNER_FRONTEND_PORT)make help lists every available target. The
Makefile is the canonical source of truth for build
commands - this file references targets that exist there; do
not invent new ones in PRs without adding them to the Makefile
in the same change.
make test # all tests (backend + plugins + frontend)
make test-backend # backend only
make test-frontend # Vitest only
make test-plugin-{name} # single plugin (assessment, session, ...)
make check-types # mypy + tsc --noEmitE2E (Playwright):
cd e2e && npx playwright test --project=smokeAdaptive Learner is a Progressive Web App. UI changes MUST be verified against the following viewport sizes — the mobile-first viewports are the user's daily experience, the iPad covers the tablet / split-screen case:
| Device | Width | Notes |
|---|---|---|
| Smallest target | 360px | Layout safety net; no horizontal scroll |
| iPhone SE | 375px | Compact iOS phones |
| iPhone 14 | 390px | Standard iOS |
| Pixel 7 | 412px | Standard Android |
| iPad | 768px | Tablet / split-screen; at the mobile breakpoint |
The e2e/smoke/mobile-viewports.spec.ts Playwright spec pins
no-horizontal-overflow + hamburger visibility + online indicator
at iPhone SE / iPhone 14 / Pixel 7 / iPad. Run it after any
CSS / layout change:
cd e2e && npx playwright test smoke/mobile-viewports.spec.tsTouch-target rule: every interactive element under the
@media (max-width: 768px) block must be ≥44x44px (Apple/Google
guideline). Inputs also need font-size: 16px to suppress
iOS-Safari focus-zoom.
Manual checks during smoke-test (handled per-release by the person tagging):
- Chrome DevTools device emulation at all 4 sizes — confirm no horizontal scroll, hamburger works, Session input is reachable.
- Chrome "Install app" prompt — open in a real browser, confirm the prompt appears and the installed app launches standalone.
- Toggle DevTools "Offline" — verify the online indicator flips,
/sessionshows the offline message, theoffline.htmlpage renders when refreshing a non-cached route. - Lighthouse audit — target PWA score > 90, Performance > 80.
Every UI feature is documented visually. Screenshots are generated with
Playwright and tracked under e2e/visual/features/ — doubling as pixel-diff
regression and a documentation gallery.
On any UI change:
- Update the screenshot spec (a
FeatureShotin theFEATURESmap ine2e/scripts/capture-feature-screenshots.ts— a kebab-case<feature>/<shot>path + asetup(page)driving the dexie preview build into the state). make capture-screenshots- Commit the PNGs:
git add e2e/visual/features/
make capture-screenshots # build dexie frontend + --update-snapshots
# review every new PNG under e2e/visual/features/, then commit it
make verify-screenshots # pixel-compare against the committed baselinesNaming convention:
- Folder: kebab-case (e.g.
matching-animation/) - Desktop:
feature.png(1280×720) - Mobile:
feature.mobile.png(375×812) - Default theme (
dark), German, realistic test data (no "Test-1" titles)
Baselines are generated + reviewed on a consistent machine (font anti-aliasing
differs between machines), not in CI. Never --update-snapshots to silence a
diff that reveals a real bug. Features Playwright can't reach (the desktop
launcher) are captured manually into the matching folder. This applies to every
PR with UI changes; pure backend / launcher / test / docs PRs are exempt. See
e2e/visual/features/README.md and
docs/developer/testing.md.
Adaptive Learner plugins are standalone Poetry packages that register through PluginForge ^0.5.0 entry points. New format-specific or workflow-specific features generally belong in a plugin, not in core.
The smallest existing plugin to copy is
plugins/adaptive-learner-plugin-getstarted/.
Mirror its shape:
plugins/adaptive-learner-plugin-yourname/
pyproject.toml # name, version, pluginforge dep, entry point
adaptive_learner_yourname/
__init__.py
plugin.py # YourPlugin(BasePlugin)
routes.py # FastAPI APIRouter (optional)
tests/ # pytest tests
README.md
Steps:
- Copy the directory; rename
adaptive-learner-plugin-getstartedandadaptive_learner_getstartedto your plugin name. - Edit
pyproject.toml: package name, description, the[tool.poetry.plugins."adaptive_learner.plugins"]entry point. - Implement
plugin.pyextendingBasePluginwithname,version,api_version = "1",license_tier = "core". Overrideactivate(),get_routes(),get_frontend_manifest()as needed. - Add a path-dep in
backend/pyproject.tomlmirroring the existing entries. - Add the plugin slug to
backend/config/app.yaml.exampleunderplugins.enabled. - If your plugin has runtime settings, drop them at
backend/config/plugins/{slug}.yaml(PluginForge reads from there, not from inside the plugin's own dir). cd backend && poetry lock && poetry install, thenmake test-plugin-{yourname}.
The plugin development guide at docs/help/en/developers/plugins.md covers the hook spec catalogue, frontend manifest slots, and ZIP-distribution layout in more depth.
All plugins currently ship under MIT with
license_tier = "core". A licensing layer in
backend/app/licensing.py exists but is dormant
(LICENSING_ENABLED = False); no plugin is gated at runtime
today. If a future plugin adopts a paid tier, source remains
public and licensing affects only runtime activation.
Project rules live in .claude/rules/ and are
read on demand:
- architecture.md - layered architecture, plugin structure, UI strategy
- coding-standards.md - naming, function design, dependency policy
- code-hygiene.md - linting, pre-commit, error-handling architecture, API conventions
- quality-checks.md - test pyramid, coverage targets, mutation testing
- lessons-learned.md - known pitfalls (TipTap, import, export, Alembic logging)
- ai-workflow.md - session workflow, documentation protocol
- release-workflow.md - release process
The pre-commit hooks (ruff, ruff-format, trailing-whitespace,
end-of-file, YAML/JSON validation) run automatically on
git commit. Install them once with
cd backend && poetry run pre-commit install.
Adaptive Learner ships in 8 languages: DE, EN, ES, FR, EL, PT, TR, JA.
Every user-facing change must add or update keys in all 8
catalogs under backend/config/i18n/{lang}.yaml. Parity tests
fail the build if a key is missing in any language.
German content (i18n catalogs, help docs, README-de) uses real
UTF-8 umlauts. ASCII transliterations like fuer, ueber,
oeffentlich are forbidden. The
scripts/find_umlaut_candidates.py and
scripts/replace_umlauts.py tooling enforces this with a
whitelist.
Adaptive Learner uses Conventional Commits. There is no commit-msg-time tool enforcing this; the convention is documentation-only and reinforced through code review.
Common types: feat, fix, refactor, docs, test,
chore. Provide a scope when one is obvious:
feat(export): ..., fix(editor): ....
Atomic commits. Each commit must leave the tree green
(make test passes); intermediate commits with broken tests
break bisect. Combine source + test changes in the same commit
when splitting them would create a red intermediate state.
- Fork the repository and clone your fork.
- Branch:
git checkout -b feat/short-nameorfix/short-name. - Make changes; keep commits atomic.
- Run
make testandmake check-typeslocally; both must be green. - Push and open a PR. The PR template asks for type, testing evidence, doc updates, and plugin impact.
For larger changes, open an issue first to discuss design. Use the Feature Request template.
Adaptive Learner follows Contributor Covenant 2.1. Reports go to asterios.raptis@web.de.
For security vulnerabilities, do not open a public issue. Use GitHub Private Vulnerability Reporting per SECURITY.md.