Compare commits

..

3 Commits

Author SHA1 Message Date
e73700763d Merge pull request 'Fix #32: gestringifieerde arrays in onboarding-overview output normaliseren' (#33) from fix/issue-32-onboarding-schema into main
All checks were successful
On Push to Main / test (push) Successful in 35s
On Push to Main / publish (push) Successful in 1m12s
On Push to Main / deploy-dev (push) Successful in 3m15s
Reviewed-on: #33
2026-07-13 07:46:53 +00:00
RaymondVerhoef
182fb2357f fix: coerce stringified arrays in onboarding overview output (#32)
All checks were successful
On Pull Request to Main / test (pull_request) Successful in 41s
On Pull Request to Main / publish (pull_request) Successful in 1m15s
On Pull Request to Main / deploy-dev (pull_request) Successful in 3m17s
Opening an onboarding theme could fail with "LLM output failed schema
validation": the fast-tier model occasionally emits the key_points and
topics_covered tool fields as JSON-encoded strings instead of real
arrays. Same failure class the quiz schema already guards against with
z.preprocess.

- onboardingOverviewSchema: parse "[...]" strings back to arrays before
  validation; split a bullet/newline string of key points as a fallback;
  keep the first 5 key points on overage instead of failing the user.
  Genuinely bad output (too few points, non-JSON strings, wrong types)
  still fails validation.
- emit_onboarding_overview tool: descriptions now state the fields must
  be JSON arrays, never strings (prompt-side nudge).
- 5 regression tests reproducing the exact #32 payload shapes
  (npm test 139/139).

Closes #32

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:38:46 +02:00
b1d3686d35 Merge pull request 'Onboarding-track: 5-daagse thema-introductie vanuit de "New here?"-kaart (#30)' (#31) from feat/onboarding-track into main
All checks were successful
On Push to Main / test (push) Successful in 34s
On Push to Main / publish (push) Successful in 1m11s
On Push to Main / deploy-dev (push) Successful in 3m19s
Reviewed-on: #31
2026-07-13 07:20:00 +00:00
15 changed files with 89 additions and 246 deletions

View File

@@ -1,8 +0,0 @@
- [ ] Whereabouts voor kantoor aanwezigheid
- [ ] Interne kennis teams
- [ ] Check Daisy labs
- [ ] DCMR hidden layer run voor AI litteracy
- [ ] AI works summit voor Evan
- [ ] Klantevemementen
- [ ] Lectures TU
- [ ] Marketing targets stellen

View File

@@ -1,232 +0,0 @@
---
title: Respellion — organisatie-inrichting
category: Inrichtingsdocument
summary: >
Beschrijvend inrichtingsdocument van Respellion: hoe de organisatie is
ingericht langs organisatiemodel, communicatiemodel, performancemodel,
coachingcultuur, beloningsstructuur (richtinggevend Baarda-model) en
opschalen/toekomstperspectief. Beperkt tot inrichting; operationele
procedures zijn bewust buiten scope gehouden.
tags:
- inrichting
- holacracy
- sociocracy
- consent
- coachingcultuur
- performancemodel
- baarda
- opschalen
status: beschrijvend inrichtingsdocument (geen operationeel beleid)
related_topics:
- who-we-are.md
- how-we-work-together.md
- roles-and-accountabilities.md
- learning-personal-development.md
---
# Respellion — organisatie-inrichting
Dit document beschrijft *hoe Respellion als organisatie is ingericht*. Het gaat
over de structuur, de spelregels en de onderliggende ontwerpkeuzes — niet over
operationele procedures zoals verlof, urenregistratie of tooling-instellingen.
Waar een uitspraak op vastgesteld handboekbeleid berust, is dat de basis; waar
een onderdeel richtinggevend en nog niet vastgesteld is, staat dat expliciet
vermeld.
Respellion is een softwareontwikkelbedrijf, opgericht in 2024 door voormalige
leiders van QDelft, met als doel "een maatschappij waarin open-source technologie
wordt ingezet om digitalisering op een duurzame manier mogelijk te maken". De
organisatie positioneert zich als *responsibly rebellious*: innovatieve
werkwijzen gecombineerd met maatschappelijke verantwoordelijkheid. Vier
kernwaarden — Trust, Courage, Self-discipline en Entrepreneurship — vormen de
grondslag onder elke inrichtingskeuze die hieronder wordt beschreven.
## 1. Organisatiemodel
Respellion is ingericht volgens **Holacracy**, een governancemodel voor
zelforganisatie zonder traditionele managementhiërarchie. Het uitgangspunt is
kernachtig samengevat als "no management, no job descriptions": er zijn geen
leidinggevende functies, alleen dynamische rollen met expliciete
verantwoordelijkheden.
Vier ontwerpkeuzes typeren het model:
- statische functieomschrijvingen worden vervangen door **dynamische rollen**;
- **gedelegeerd gezag** wordt vervangen door **gedistribueerd gezag** — autoriteit
ligt bij de rol, niet bij een persoon of een baas;
- grote reorganisaties worden vervangen door **continue micro-iteraties**;
- kantoorpolitiek wordt vervangen door **transparante regels en gedisciplineerde
processen**.
De organisatie is opgebouwd uit geneste **circles**: de *Anchor Circle* met
daaronder *Operations* en daaronder weer *Software Delivery*. Rollen zijn
geclusterd in de circle waar ze thuishoren. Vier structurele rollen zijn
constitutioneel verplicht en keren in elke circle terug: **Circle Lead** (houdt de
purpose van de circle), **Facilitator** (bewaakt proces en governance),
**Secretary** (borgt besluiten en administratie) en **Circle Rep** (kanaliseert
spanningen naar de bredere circle). Daarnaast bestaan tal van operationele rollen
(zoals People Officer, Privacy Officer, Process Guardian, Treasury Keeper) die
elk een eigen purpose en accountabilities dragen.
De motor onder het model is de **tension**: het ervaren verschil tussen de huidige
en een betere situatie. Iedereen kan een tension inbrengen; die wordt verwerkt in
een tactical of governance meeting en leidt tot een concrete actie of een
rolwijziging. Zo evolueert de structuur voortdurend van onderop, in plaats van via
periodieke reorganisaties. Een oprichter vat de houding samen als *tuinieren in
plaats van bouwen*: condities scheppen voor groei, niet bouwen naar een rigide
blauwdruk. De actuele rolinvulling is voor iedereen transparant vastgelegd in
GlassFrog.
## 2. Communicatiemodel
Het communicatiemodel is de directe consequentie van gedistribueerd gezag: als
niemand "de baas" is, moeten afstemming en besluitvorming via heldere,
transparante gremia verlopen. Respellion kent daarvoor twee vergaderlijnen, een
besluitvormingsnorm en een aantal cultuurafspraken.
**Twee vergaderlijnen.** *Tactical meetings* richten zich op operationele
coördinatie en resulteren in concrete taken, toegewezen aan rollen en bijgehouden
op het taakbord. *Governance meetings* richten zich op de structuur zelf: rollen
worden er gecreëerd, gewijzigd of opgeheven om spanningen op te lossen. Beide
worden vastgelegd in Microsoft Loop, zodat besluiten en hun onderbouwing
naspeurbaar blijven.
**Besluitvorming op basis van consent.** Onder Holacracy ligt de oudere
**sociocratische** traditie, en Respellion houdt die laag bewust zichtbaar. Een
besluit komt tot stand op basis van *commitment/consent* — niet op basis van
consensus en niet via meerderheid. Het leidende principe is dat een voorstel
doorgaat zolang er geen onderbouwd bezwaar is, en dat *iedereen die door een
besluit wordt geraakt daar invloed op moet kunnen uitoefenen*. In de woorden van
de organisatie: "de stem van de minderheid wordt altijd in aanmerking genomen".
Sociocracy voegt daar een pragmatisch principe aan toe dat naadloos bij de
rebelse cultuur past — *verander alleen wat nodig is en start waar de spanning
zit* — wat verklaart waarom Respellion evolueert in kleine stappen in plaats van
in grote ontwerpen. Deze consent-laag is wat gedistribueerd gezag legitiem en
gedragen maakt; zonder consent zou autonomie verworden tot willekeur.
**Cultuurafspraken.** De waarde Trust vertaalt zich in een expliciete norm: "we
praten mét elkaar, niet óver elkaar", en spanningen worden direct benoemd in
plaats van te laten sudderen. De transparantie van GlassFrog (wie welke rol
vervult) en Loop (welke besluiten er zijn genomen) maakt dat de organisatie
zonder hiërarchische informatiestroom toch afgestemd blijft.
## 3. Performancemodel
Respellion heeft het klassieke performancemanagement — periodieke beoordeling met
oordeel en sanctie — bewust vervangen door een **continue, ontwikkelingsgerichte
benadering**. Prestatie wordt niet één keer per jaar beoordeeld, maar doorlopend
besproken in de coachingcultuur (zie §4).
De inhoudelijke maatstaf zijn de **5 Pillars of Value**, die samen bepalen welke
waarde iemand toevoegt. De pijlers wegen bewust verschillend:
- **A. Basiservaring & expertise** (weging 100%) — fundamentele vakkennis en
vaardigheden;
- **B. Rolvervulling & verantwoordelijkheid** (75%) — het eigenaarschap in de
holacratische rollen;
- **C. Impact & resultaat** (80%) — de tastbare waarde voor klanten en purpose;
- **D. Ontwikkeling & leercurve** (100%) — groei op intellectueel, emotioneel en
fysiek vlak, gekoppeld aan het persoonlijk ontwikkelplan;
- **E. Cultuur & samenwerking** (80%) — bijdrage aan het team en het naleven van
de waarden.
De beoordeling verloopt via **self-evaluation**: medewerkers scoren zichzelf op de
vijf pijlers, waarna collega's dat oordeel tijdens de Huddle Elevators actief
challengen om het compleet en eerlijk te houden. Op organisatieniveau is dit
verankerd in het ISO 9001-kwaliteitssysteem, waar de klassieke *management review*
is vervangen door een strategische review in de Anchor Circle en waar een formele
review van rolvervulling twee keer per jaar plaatsvindt. Het uitgangspunt daarbij
is dat mensen "ons belangrijkste kapitaal" zijn.
## 4. Coachingcultuur
De coachingcultuur is het hart van de mensgerichte inrichting en de drager van het
performancemodel. Het is een cultuur van continue feedback, ondersteuning en
begeleiding, gericht op ontwikkeling in plaats van beoordeling. De cultuur is
ingericht met vier vaste elementen.
**Huddle Check-ins.** Twee keer per week komen kleine, vaste groepen bij elkaar
voor snelle verbinding, updates en onderlinge steun. De frequentie en het vaste
karakter maken de verbinding betekenisvoller dan klassieke mentoring.
**Huddle Elevators.** Elke twee maanden vindt een diepere, gefaciliteerde
ontwikkelsessie plaats. De "elevator"-gedachte legt de nadruk op *voortbouwen op
sterktes* in plaats van alleen zwaktes repareren. Hier vindt ook de
self-evaluation van het performancemodel plaats.
**Capacity Building Framework.** Ontwikkeling is holistisch ingericht in drie
domeinen: **Professional** (vakinhoudelijke en intellectuele vaardigheden),
**Emotional** (sociale vaardigheden, zelfreflectie, afstemming van persoonlijke
waarden op het werk) en **Physical** (gezonde leefstijl).
**Persoonlijk Ontwikkelplan (POP).** Elke medewerker stelt een POP op, gebaseerd
op **Ofman's kernkwadrant** (kernkwaliteit → valkuil → uitdaging → allergie). Het
plan wordt gedeeld op een Miro-bord en besproken tijdens de Elevators.
Twee kenmerken maken de cultuur werkbaar. Ten eerste zijn **openheid en
kwetsbaarheid** een expliciete voorwaarde: medewerkers worden aangemoedigd open te
zijn over ontwikkelpunten, zodat collega's beter kunnen ondersteunen. Ten tweede
worden **fouten gevierd** — de "Most Rebellious Failures" bij de tweemaandelijkse
celebration maken van leren-door-falen een norm en versterken de psychologische
veiligheid.
## 5. Beloningsstructuur
De beloningsfilosofie is een directe vertaling van de kernwaarden: **belonen naar
toegevoegde waarde, niet naar dienstjaren of functietitel.** De inrichting bestaat
uit een vastgesteld deel en een richtinggevend deel.
**Vastgesteld — Growth & Reward.** De salarisontwikkeling volgt de prestatie op de
5 Pillars of Value. De self-evaluationscore wordt gekoppeld aan een **bandingmodel**
met oplopende verhogingscategorieën (bijvoorbeeld 02%, 24%, enzovoort), zodat
bovengemiddelde prestatie ook bovengemiddeld wordt beloond in plaats van een
automatische verhoging voor iedereen. Bij het bepalen van de banden wegen de
financiële gezondheid van het bedrijf, de inflatie (CPI) en de marktconformiteit
mee; instroommoment binnen het jaar wordt pro-rata verrekend. De banding wordt ten
slotte over de huddles heen gevalideerd om het beeld eerlijk en consistent te
houden.
**Richtinggevend, nog niet vastgesteld — het Baarda-model.** Als volgende stap in
de beloningsstructuur verkent Respellion een **IT-geoptimaliseerde Baarda-ladder**
(conceptdocument "Optie 3"). Dit model is gebaseerd op Rolf Baarda's principe
*toegevoegde waarde = kennis × probleemoplossend vermogen* (TW = K × POV). Kern is
dat het **niveau van een medewerker uitsluitend wordt bepaald door
probleemoplossend vermogen** — niet door dienstjaren, opleiding of titel. De ladder
kent acht niveaus die de Baarda-rollen vertalen naar IT-disciplines (full stack
developers, QA-engineers, business analisten en agile coaches), met per niveau een
tariefklasse-indicatie die in detacheringscontext als kwalificatie richting
klanten dient. Dit onderdeel is expliciet **conceptueel en richtinggevend**: er
liggen nog open besluiten over onder meer de naamgeving, de eenmalige initiële
plaatsing van medewerkers, de te gebruiken marktbenchmark en de interne validatie
van de gedragsindicatoren.
## 6. Opschalen en toekomstperspectief
De inrichting is ontworpen om te groeien **zonder managementlagen toe te voegen**.
Respellion groeit van 15 teamleden (2025) naar een beoogde 30 (2026), met als doel
complete, inzetbare ontwikkelteams te kunnen leveren.
Twee mechanismen maken die schaalbaarheid mogelijk. Ten eerste **circle-nesting**:
zodra de complexiteit binnen een circle toeneemt, splitst een deel zich af als
sub-circle met eigen rollen en structurele bezetting. De bestaande gelaagdheid
(Anchor → Operations → Software Delivery) is daar al de uitdrukking van; verdere
groei betekent meer geneste circles, niet meer hiërarchie. Ten tweede biedt de
**Baarda-ladder** — zodra vastgesteld — een schaalbaar beloningsraamwerk dat ook
bij groei intern vergelijkbaar en naar klanten uitlegbaar blijft, doordat elk
niveau een objectieve niveau-kwalificatie geeft.
De consent- en tension-mechanismen (§1 en §2) zijn intrinsiek schaalbaar: ze
verwerken verandering lokaal en continu, waardoor de organisatie ook bij groei
adaptief blijft zonder centrale sturing. Het toekomstperspectief is daarmee een
organisatie die in omvang verdubbelt terwijl ze haar platte, zelforganiserende
karakter en haar maatschappelijke, open-source purpose behoudt.
## Samenhang
De zes onderdelen vormen één consistente logica. **Consent** legitimeert het
gedistribueerde gezag van **Holacracy**; het **communicatiemodel** maakt dat gezag
werkbaar; het **performancemodel** en de **coachingcultuur** houden de mensen
erin gezond en in ontwikkeling; de **beloningsstructuur** zorgt dat toegevoegde
waarde — niet anciënniteit — telt; en het **opschaalmodel** laat dit alles groeien
zonder de platte structuur op te geven. Alle zes wortelen in de vier kernwaarden:
Trust, Courage, Self-discipline en Entrepreneurship.

View File

@@ -9,6 +9,7 @@ import {
customTopicSchema, customTopicSchema,
graphActionsSchema, graphActionsSchema,
proposeGraphDeltaSchema, proposeGraphDeltaSchema,
onboardingOverviewSchema,
} from '../llmSchemas'; } from '../llmSchemas';
const sampleTopic = { const sampleTopic = {
@@ -192,3 +193,56 @@ describe('proposeGraphDeltaSchema', () => {
).toThrow(); ).toThrow();
}); });
}); });
describe('onboardingOverviewSchema (issue #32 — fast-tier stringified arrays)', () => {
const base = {
title: 'Privacy',
what_it_is: 'How we handle data.',
why_it_matters: 'You touch personal data weekly.',
};
const points = ['Point one', 'Point two', 'Point three'];
const topics = [{ topic_id: 'avg', label: 'AVG' }];
it('accepts well-formed output', () => {
const r = onboardingOverviewSchema.parse({ ...base, key_points: points, topics_covered: topics });
expect(r.key_points).toEqual(points);
expect(r.topics_covered).toEqual(topics);
});
it('coerces JSON-stringified arrays back to arrays (the exact #32 failure)', () => {
const r = onboardingOverviewSchema.parse({
...base,
key_points: JSON.stringify(points),
topics_covered: JSON.stringify(topics),
});
expect(r.key_points).toEqual(points);
expect(r.topics_covered).toEqual(topics);
});
it('splits a bullet/newline string of key points as a fallback', () => {
const r = onboardingOverviewSchema.parse({
...base,
key_points: '- Point one\n• Point two\n3. Point three',
topics_covered: topics,
});
expect(r.key_points).toEqual(points);
});
it('keeps the first 5 key points instead of failing on overage', () => {
const seven = Array.from({ length: 7 }, (_, i) => `P${i + 1}`);
const r = onboardingOverviewSchema.parse({ ...base, key_points: seven, topics_covered: topics });
expect(r.key_points).toEqual(['P1', 'P2', 'P3', 'P4', 'P5']);
});
it('still rejects genuinely bad output', () => {
expect(() =>
onboardingOverviewSchema.parse({ ...base, key_points: ['only', 'two'], topics_covered: topics }),
).toThrow();
expect(() =>
onboardingOverviewSchema.parse({ ...base, key_points: points, topics_covered: 'not json at all' }),
).toThrow();
expect(() =>
onboardingOverviewSchema.parse({ ...base, key_points: 42, topics_covered: topics }),
).toThrow();
});
});

View File

@@ -229,14 +229,43 @@ export const themeSessionSchema = z.object({
keyTakeaways: z.array(z.string().min(1)).min(3), keyTakeaways: z.array(z.string().min(1)).min(3),
}); });
// Fast-tier models occasionally emit array tool fields as a JSON-encoded
// string ("[\"a\",\"b\"]") instead of a real array (issue #32). Parse those
// back to arrays before validating; leave anything else untouched so real
// type errors still fail validation.
function coerceStringifiedArray(v) {
if (typeof v === 'string') {
const s = v.trim();
if (s.startsWith('[')) {
try { return JSON.parse(s); } catch { /* keep original, let Zod report */ }
}
}
return v;
}
const onboardingKeyPoints = z.preprocess((v) => {
let out = coerceStringifiedArray(v);
// Fallback: a bullet/newline list as one string → split into points.
if (typeof out === 'string' && out.includes('\n')) {
out = out
.split('\n')
.map((line) => line.replace(/^\s*[-•*\d.]+\s*/, '').trim())
.filter(Boolean);
}
// Overage is trivial — keep the first 5 rather than failing the user.
if (Array.isArray(out) && out.length > 5) out = out.slice(0, 5);
return out;
}, z.array(z.string().min(1)).min(3).max(5));
export const onboardingOverviewSchema = z.object({ export const onboardingOverviewSchema = z.object({
title: z.string().min(1), title: z.string().min(1),
what_it_is: z.string().min(1), what_it_is: z.string().min(1),
why_it_matters: z.string().min(1), why_it_matters: z.string().min(1),
key_points: z.array(z.string().min(1)).min(3).max(5), key_points: onboardingKeyPoints,
topics_covered: z topics_covered: z.preprocess(
.array(z.object({ topic_id: z.string().min(1), label: z.string().min(1) })) coerceStringifiedArray,
.min(1), z.array(z.object({ topic_id: z.string().min(1), label: z.string().min(1) })).min(1),
),
}); });
/** /**

View File

@@ -470,11 +470,11 @@ export const EMIT_ONBOARDING_OVERVIEW_TOOL = {
items: { type: 'string' }, items: { type: 'string' },
minItems: 3, minItems: 3,
maxItems: 5, maxItems: 5,
description: '35 short, concrete takeaways a newcomer should remember about this theme.', description: '35 short, concrete takeaways a newcomer should remember about this theme. Must be a JSON array of strings — never a single string.',
}, },
topics_covered: { topics_covered: {
type: 'array', type: 'array',
description: 'The topics that make up this theme. Reuse the exact topic_id you were given so the UI can link back.', description: 'The topics that make up this theme. Reuse the exact topic_id you were given so the UI can link back. Must be a JSON array of objects — never a string.',
items: { items: {
type: 'object', type: 'object',
properties: { properties: {