Compare commits
2 Commits
feat/onboa
...
fix/issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
182fb2357f | ||
| b1d3686d35 |
@@ -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
|
|
||||||
@@ -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 0–2%, 2–4%, 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.
|
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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: '3–5 short, concrete takeaways a newcomer should remember about this theme.',
|
description: '3–5 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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user