Een zelf-gepacete 5-daagse onboarding-track die een nieuwe medewerker in de breedte (niet de diepte) kennis laat maken met alle KB-thema's, zodat ze de dagelijkse/wekelijkse werkprocessen van Respellion begrijpen. Aangeboden als CTA in de bestaande "New here?"-kaart op het Dashboard; altijd beschikbaar, los van enrollment.
Ontwerp
Thema = trackbare eenheid; "dag" = presentatie-groepering (berekend bij het lezen) → herindeling verliest nooit voortgang. Voltooiing per thema in onboarding_completions.
Lazy, gecachte, lichte AI-content: fast-tier emit_onboarding_overview per thema (wat het is / waarom het ertoe doet voor dagelijks/wekelijks werk / kernpunten / topics), gecached in onboarding_overviews op thema + topics_fingerprint (hergeneratie bij gewijzigde topic-set).
Route /onboarding-track via de bestaande skipEnrollmentGate-prop, ontkoppeld van het 26-weken-curriculum (los van /onboarding, de enrollment-pagina).
Twee nieuwe collections met authenticated-only rules + unieke indexes; TEXT team_member_id (post-#18/#27-conventie).
Verificatie
22 nieuwe unit-tests voor de pure helpers → npm test134/134.
ESLint schoon op alle gewijzigde bestanden; npm run build OK.
PocketBase v0.30.4 boot past migratie 1781200000 toe — beide collections aanwezig met de juiste unieke indexes en @request.auth.id != ""-rules (geverifieerd via de API).
Backend-contract: upsert-idempotentie, unieke-index-guard (dubbele create → 400), en veilige filtering op thema's met speciale tekens (&, spaties) via pb.filter().
Reviewpunten
.github/workflows/ niet aangeraakt. De bekende falende caddy validate-stap (#26/PR #28/#29) staat los hiervan.
Content-taal is Engels, passend bij de "New here?"-kaart.
Geen leaderboard-punten en geen afsluitende quiz (bewuste productkeuze).
Handmatige E2E (na deploy)
Dashboard → "New here?" → CTA 0/5 days → /onboarding-track → thema openen (overview genereert, "Mark as done") → chip loopt op → alles klaar → afrondingsscherm + "Onboarding complete". Optioneel localStorage['admin:use_simulation']=true om de AI-call te stubben.
Closes #30
## Wat & waarom
Een zelf-gepacete **5-daagse onboarding-track** die een nieuwe medewerker in de breedte (niet de diepte) kennis laat maken met **alle** KB-thema's, zodat ze de dagelijkse/wekelijkse werkprocessen van Respellion begrijpen. Aangeboden als CTA in de bestaande **"New here?"**-kaart op het Dashboard; altijd beschikbaar, los van enrollment.
## Ontwerp
- **Thema = trackbare eenheid; "dag" = presentatie-groepering** (berekend bij het lezen) → herindeling verliest nooit voortgang. Voltooiing per thema in `onboarding_completions`.
- **Lazy, gecachte, lichte AI-content**: fast-tier `emit_onboarding_overview` per thema (wat het is / waarom het ertoe doet voor dagelijks/wekelijks werk / kernpunten / topics), gecached in `onboarding_overviews` op thema + `topics_fingerprint` (hergeneratie bij gewijzigde topic-set).
- **Route `/onboarding-track`** via de bestaande `skipEnrollmentGate`-prop, ontkoppeld van het 26-weken-curriculum (los van `/onboarding`, de enrollment-pagina).
- Twee nieuwe collections met authenticated-only rules + unieke indexes; TEXT `team_member_id` (post-#18/#27-conventie).
## Verificatie
- **22 nieuwe unit-tests** voor de pure helpers → `npm test` **134/134**.
- **ESLint** schoon op alle gewijzigde bestanden; **`npm run build`** OK.
- **PocketBase v0.30.4 boot** past migratie `1781200000` toe — beide collections aanwezig met de juiste **unieke indexes** en **`@request.auth.id != ""`-rules** (geverifieerd via de API).
- **Backend-contract**: upsert-idempotentie, unieke-index-guard (dubbele create → 400), en veilige filtering op thema's met speciale tekens (`&`, spaties) via `pb.filter()`.
## Reviewpunten
- `.github/workflows/` niet aangeraakt. De bekende falende `caddy validate`-stap (#26/PR #28/#29) staat los hiervan.
- Content-taal is Engels, passend bij de "New here?"-kaart.
- Geen leaderboard-punten en geen afsluitende quiz (bewuste productkeuze).
## Handmatige E2E (na deploy)
Dashboard → "New here?" → CTA `0/5 days` → `/onboarding-track` → thema openen (overview genereert, "Mark as done") → chip loopt op → alles klaar → afrondingsscherm + "Onboarding complete". Optioneel `localStorage['admin:use_simulation']=true` om de AI-call te stubben.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
A self-paced onboarding track that introduces a new employee to every KB
theme in breadth (not depth), so they grasp how Respellion works day to
day and week to week. Offered as a CTA inside the Dashboard "New here?"
explainer card; always available regardless of enrollment.
Design:
- Theme is the trackable unit; the 5 "days" are a read-time presentation
grouping, so re-chunking never loses progress. Completion is stored per
theme in onboarding_completions.
- Per-theme overview generated lazily on first open (fast-tier
emit_onboarding_overview tool), cached in onboarding_overviews keyed by
theme + a topics_fingerprint that triggers regeneration when the theme's
topic set changes.
- Reachable via /onboarding-track using the existing skipEnrollmentGate
prop, decoupled from the 26-week curriculum (distinct from /onboarding,
the enrollment page).
Backend:
- pb_migrations/1781200000_created_onboarding.js: two collections with
authenticated-only rules and unique indexes; TEXT team_member_id (no
relation) per the post-#18/#27 convention. Mirrored in
scripts/setup-pb-collections.mjs.
- src/lib/onboardingService.js: pure helpers (orderThemes,
distributeThemesIntoDays, computeTopicsFingerprint,
computeOnboardingProgress, buildOnboardingPlan) + generation + I/O.
- db.js onboarding helpers use pb.filter() bindings (theme is free text).
- LLM tool + Zod schema + registry + simulation stub.
Frontend:
- src/pages/OnboardingTrack.jsx (day list, per-theme overview, completion
banner, progress ring/day bar).
- Dashboard "New here?" card CTA + X/5-days progress chip (hidden when the
KB has no themes).
Docs: data-model, generation-spec (§D), frontend-spec updated.
Verified: 22 new unit tests (npm test 134/134), eslint clean on changed
files, npm run build OK, PocketBase v0.30.4 boot applies the migration
(collections + unique indexes + authed rules confirmed), and a backend
contract check (upsert idempotency, unique-index guard, special-char
theme filtering).
Closes#30
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The caddy-validate step added for #26 (and merged to main via #29) used a
`-v "$PWD":/workspace` bind mount, which does not propagate to the sibling
Docker daemon in the Gitea act runner — the container saw an empty dir and
every PR failed with "open Caddyfile: no such file or directory".
Validate instead inside the freshly built image, where the production
Caddyfile lives at /etc/caddy/Caddyfile: no bind mount, and it checks the
exact file that ships. Caddyfile.test keeps being covered by the homepage
smoke test (its container must serve 200).
Closes#26
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pipeline groen (run #101). test ✅ · publish ✅ · deploy-dev ✅ — PLAY RECAP failed=0, beide containers Up, proxy-health 200.
Twee dingen om te weten bij review:
CI-fix meegenomen (Closes#26). De eerste run (#100) faalde op de caddy validate-stap — niet door deze feature, maar door de bind-mount-bug uit #26 die via de merge van #29 op main terechtkwam (open Caddyfile: no such file or directory, mount propageert niet naar de sibling Docker-daemon in de Gitea-runner). Die stap blokkeerde élke PR naar main. Opgelost door de productie-Caddyfile te valideren binnen het gebouwde image (/etc/caddy/Caddyfile), zonder mount. Dit maakt PR #28 overbodig — die kan dicht.
Migratie toegepast op Labs. PocketBase crash-loopt bij een migratiefout (zie #18); de container is healthy en de deploy-health-gate is groen, dus 1781200000 is schoon toegepast. Lokaal bovendien geverifieerd: beide collections aanwezig met unieke indexes + @request.auth.id != ""-rules, plus een backend-contracttest (upsert-idempotentie, unieke-index-guard, filtering op thema's met speciale tekens).
Handmatige E2E (na merge/als je nu op Labs kijkt): Dashboard → "New here?" → CTA 0/5 days → /onboarding-track → thema openen → "Mark as done" → chip loopt op → afrondingsscherm. Zet eventueel localStorage['admin:use_simulation']=true om de AI-call te stubben.
**Pipeline groen (run #101).** test ✅ · publish ✅ · deploy-dev ✅ — PLAY RECAP `failed=0`, beide containers Up, proxy-health 200.
Twee dingen om te weten bij review:
1. **CI-fix meegenomen (Closes #26).** De eerste run (#100) faalde op de `caddy validate`-stap — niet door deze feature, maar door de bind-mount-bug uit #26 die via de merge van #29 op `main` terechtkwam (`open Caddyfile: no such file or directory`, mount propageert niet naar de sibling Docker-daemon in de Gitea-runner). Die stap blokkeerde élke PR naar main. Opgelost door de productie-Caddyfile te valideren *binnen* het gebouwde image (`/etc/caddy/Caddyfile`), zonder mount. Dit maakt PR #28 overbodig — die kan dicht.
2. **Migratie toegepast op Labs.** PocketBase crash-loopt bij een migratiefout (zie #18); de container is healthy en de deploy-health-gate is groen, dus `1781200000` is schoon toegepast. Lokaal bovendien geverifieerd: beide collections aanwezig met unieke indexes + `@request.auth.id != ""`-rules, plus een backend-contracttest (upsert-idempotentie, unieke-index-guard, filtering op thema's met speciale tekens).
Handmatige E2E (na merge/als je nu op Labs kijkt): Dashboard → "New here?" → CTA `0/5 days` → `/onboarding-track` → thema openen → "Mark as done" → chip loopt op → afrondingsscherm. Zet eventueel `localStorage['admin:use_simulation']=true` om de AI-call te stubben.
rve
merged commit b1d3686d35 into main2026-07-13 07:20:01 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #30
Wat & waarom
Een zelf-gepacete 5-daagse onboarding-track die een nieuwe medewerker in de breedte (niet de diepte) kennis laat maken met alle KB-thema's, zodat ze de dagelijkse/wekelijkse werkprocessen van Respellion begrijpen. Aangeboden als CTA in de bestaande "New here?"-kaart op het Dashboard; altijd beschikbaar, los van enrollment.
Ontwerp
onboarding_completions.emit_onboarding_overviewper thema (wat het is / waarom het ertoe doet voor dagelijks/wekelijks werk / kernpunten / topics), gecached inonboarding_overviewsop thema +topics_fingerprint(hergeneratie bij gewijzigde topic-set)./onboarding-trackvia de bestaandeskipEnrollmentGate-prop, ontkoppeld van het 26-weken-curriculum (los van/onboarding, de enrollment-pagina).team_member_id(post-#18/#27-conventie).Verificatie
npm test134/134.npm run buildOK.1781200000toe — beide collections aanwezig met de juiste unieke indexes en@request.auth.id != ""-rules (geverifieerd via de API).&, spaties) viapb.filter().Reviewpunten
.github/workflows/niet aangeraakt. De bekende falendecaddy validate-stap (#26/PR #28/#29) staat los hiervan.Handmatige E2E (na deploy)
Dashboard → "New here?" → CTA
0/5 days→/onboarding-track→ thema openen (overview genereert, "Mark as done") → chip loopt op → alles klaar → afrondingsscherm + "Onboarding complete". OptioneellocalStorage['admin:use_simulation']=trueom de AI-call te stubben.🤖 Generated with Claude Code
Pipeline groen (run #101). test ✅ · publish ✅ · deploy-dev ✅ — PLAY RECAP
failed=0, beide containers Up, proxy-health 200.Twee dingen om te weten bij review:
caddy validate-stap — niet door deze feature, maar door de bind-mount-bug uit #26 die via de merge van #29 opmainterechtkwam (open Caddyfile: no such file or directory, mount propageert niet naar de sibling Docker-daemon in de Gitea-runner). Die stap blokkeerde élke PR naar main. Opgelost door de productie-Caddyfile te valideren binnen het gebouwde image (/etc/caddy/Caddyfile), zonder mount. Dit maakt PR #28 overbodig — die kan dicht.1781200000is schoon toegepast. Lokaal bovendien geverifieerd: beide collections aanwezig met unieke indexes +@request.auth.id != ""-rules, plus een backend-contracttest (upsert-idempotentie, unieke-index-guard, filtering op thema's met speciale tekens).Handmatige E2E (na merge/als je nu op Labs kijkt): Dashboard → "New here?" → CTA
0/5 days→/onboarding-track→ thema openen → "Mark as done" → chip loopt op → afrondingsscherm. Zet eventueellocalStorage['admin:use_simulation']=trueom de AI-call te stubben.