# Prompt: procesbeschrijving → SmartMapped-scenario (JSON)

Plak alles hieronder (vanaf de lijn) in een LLM en zet je procesbeschrijving onderaan op de plek van `[PLAK HIER JE PROCESBESCHRIJVING]`. Het antwoord is één ```json-codeblok; plak dat (met of zonder de backticks) in de app via **Nieuwe flow → Vanuit JSON** of **JSON State Tree → Apply**.

---

Je bent scenario-ontwerper voor **SmartMapped**, een 3D-visualisatietool in de stijl van een isometrische miniatuurwereld die een proces uitlegt als een kleine wereld: ruimtes met een vloerraster, stations (nodes) verbonden door paden, figuurtjes (actors) die over die paden lopen en dingen (payloads) dragen, en een tijdlijn die stap voor stap afspeelt.

Zet de procesbeschrijving onderaan om in **één JSON-object** volgens het schema hieronder.

## Uitvoer

- Antwoord met **precies één codeblok** dat begint met ```json en eindigt met ```. Geen tekst ervoor of erna.
- Strikte JSON: geen commentaar, geen trailing commas, dubbele aanhalingstekens.
- **Vertaal nooit sleutels of vaste waarden.** Alle veldnamen en alle waarden tussen backticks in dit document (`geometry`, `variant`, `shape`, `style`, `type`, `holderType`, `theme`, …) zijn Engels en neem je letterlijk over: `"computer"`, niet `"computerbureau"`; `"antenna"`, niet `"antenne"`; `"move_actor"`, niet `"verplaats_actor"`. Alleen leesbare tekst (`name`, `label`, `sublabel`, `description`, `title`, `reply`, `timeLabel`, tak-`label`) is in het Nederlands. Eén ongeldige waarde en de app weigert het hele scenario.

## 1. Werkwijze (eerst in je hoofd)

1. **Ruimtes**: één hoofdruimte (de wereld), eventueel zones (sub-ruimtes) en, alleen als het echt nodig is, niveaus om in te zoomen (§4). Begin plat: alles op één niveau. Heeft een organisatie of systeem een eigen rol in het verhaal, geef het dan een eigen zone, ook als dat de derde of vierde wordt.
2. **Nodes**: elke stap, rol of systeem is één node. Kies `geometry` op de **rol** van de stap (tabel §5), niet op de technologie. Begin bij wie het proces echt start op een `trigger` of `person`; eindig goed op `end`, fout op `reject`. Raadpleegt een stap een systeem (voorraad, klantgegevens), maak dat systeem een eigen node.
3. **Paden**: elke route die in de tijdlijn gelopen of gereden wordt is een pad. Niet alles beweegt: bellen, een API-call, een melding of iets opzoeken in een database is `send_signal`, zonder pad. Komt er iets terug, zet het antwoord in dezelfde stap (`reply`), niet in een tweede signaal. Een pad naar een `reject` is rood (`#dc2626`) en `solid`.
4. **Actors**: meestal één, bij parallelle takken één per tak. **Scheid data en fysiek**: een systeem dat een order of bericht afhandelt is een `robot`/`packet` met een `envelope`/`data-block`; een mens met goederen is een `avatar` met een `box`; vervoer is `cart` (vrachtwagen) of `car`.
5. **Payloads**: wat wordt gedragen en hoe verandert het? Maak van elke omzetting een eigen stap (order → picklijst bij een `transform`). Niets verdwijnt stilletjes: bij een splitsing (deellevering, kopie) maak je een tweede payload die buiten beeld begint (`holderType: "space"`) en bij de splitsing verschijnt. Een `splitter` is een **bewuste** splitsing. Gaat één gebeurtenis naar iedereen die luistert (een event met meerdere listeners, een melding naar alle abonnees), gebruik dan een `antenna` (`broadcast` of `pubsub`) met één `send_signal` per ontvanger in dezelfde stap.
6. **Tijdlijn**: 5–15 stappen met elk één begrijpelijke zin. Bij een echte keuze een **vertakking** (§7). Verspringt de tijd (een volgende dag, weken later), zet dan `timeLabel` op die stap. Kan een stap mislukken, maak er dan een `decision` van met een foutroute naar een `reject`.
7. **Layout**: leesrichting links → rechts en/of boven → onder, minstens één lege cel tussen verbonden nodes, opeenvolgende nodes bij voorkeur in dezelfde rij of kolom. **Paden kruisen elkaar niet**: komt de flow terug bij het begin, leg de nodes in een ring. Bij drie uitkomsten van een beslissing: middelste recht ernaast, de andere twee erboven en eronder in dezelfde kolom.

## 2. Topniveau

```
{
  "version": "1.0",
  "meta": { "title": "Korte titel", "description": "Eén of twee zinnen.", "activeSpaceId": "space_world", "cameraZoom": 22, "gridSize": 2 },
  "spaces": { "<id>": Space },
  "nodes": { "<id>": Node },
  "paths": { "<id>": Path },
  "actors": { "<id>": Actor },
  "payloads": { "<id>": Payload },
  "timeline": { "currentTime": 0, "duration": <hoogste time>, "isPlaying": false, "speed": 1, "currentStepIndex": 0, "keyframes": [ Keyframe ] }
}
```

- De vijf collecties zijn **objecten met het id als sleutel**, en datzelfde id staat ook als `"id"` in het object. Ze zijn verplicht, ook als ze leeg zijn (`{}`).
- Ids: kleine letters, cijfers, underscores, met prefix `space_`, `n_`, `p_`, `actor_`, `pl_`, `kf_`. Elke verwijzing wijst naar een bestaand id.
- Kleuren zijn hex-strings (`"#60a5fa"`). `cameraZoom` 14–26 (kleiner = verder weg; zie §3 voor grote werelden).

## 3. Raster

- `x` = links/rechts, `z` = diepte, `y` = hoogte. Alle posities zijn wereldcoördinaten, ook in sub-ruimtes.
- Een cel is 2 eenheden: celgrenzen op **even** getallen, celmiddens op **oneven** getallen.
- **Ruimte**: `position` is het midden; `width`/`depth` zijn veelvouden van 2 en de randen (`x ± width/2`, `z ± depth/2`) vallen op even getallen (width 12 rond x 0 ✓, width 10 rond x 0 ✗). Hoofdruimte typisch 20×14 tot 34×24. Met zones: reken per zone zo'n 10–12 breed (plus een lege cel ertussen) en laat de hoofdruimte meegroeien, tot ongeveer 48×30. Zet `cameraZoom` dan lager (vanaf 30 breed ongeveer 18–20, vanaf 40 breed ongeveer 14–16).
- **Node**: `x` en `z` oneven, binnen de randen van zijn ruimte, `y` = 0, **nooit twee nodes in één cel**.

## 4. Space

```
{ "id": "space_world", "name": "Naam",
  "parentId": "space_world",                       // alleen bij sub-ruimtes
  "dimensions": { "width": 28, "depth": 18, "height": 8 },
  "position": { "x": 0, "y": 0, "z": 0 },          // sub-ruimte: y 0.1
  "visuals": { "floorColor": "#1f2330", "gridColor": "#3a445c", "theme": "minimal" } }
```

- `theme` (optioneel): `cyberpunk` | `retro` | `blueprint` | `minimal` | `paper`. Optioneel ook `wallColor`, `opacity` (0–1).
- Precies **één** ruimte zonder `parentId`; `meta.activeSpaceId` wijst ernaar.
- **Zone**: sub-ruimte binnen de rechthoek van zijn parent, niet overlappend met andere zones (twee organisaties, client/server, afdelingen).
- **Niveau** (inzoomen in een gebouw, systeem of laag): een sub-ruimte waar een node met `"targetSpaceId"` naar wijst. Die node is de deur (buitenkant): `external` variant `shop`/`company`, `dock`, `person` variant `home` of `subprocess`. Het niveau krijgt een **eigen stuk vloer ver buiten de parent** (bijv. x rond 80, 150, −80), want je ziet er maar één tegelijk. Het buitenste niveau gaat over plekken (huizen, gebouwen, straten); werkstappen horen binnen.
- **Maak een niveau alleen voor een flink deelproces: minstens 4, liever 5 of meer nodes die er echt binnen gebeuren.** Een niveau met 2 of 3 stappen is **verboden**: elke sprong naar binnen en terug kost de kijker overzicht, en zo'n kort stuk maakt de flow onnodig ingewikkeld. Zet die stappen gewoon op het bovenliggende niveau (eventueel in een zone), en gebruik bij twijfel geen niveau. Meestal zijn 0 of 1 niveaus genoeg, zelden meer dan 2.
- **Een pad blijft binnen één niveau.** Tussen niveaus: data met `send_signal` plus `transfer_payload` met alleen `toNodeId`; een actor met `move_actor` zonder `pathId` naar een node op het andere niveau. Het beeld volgt vanzelf het diepste niveau waar alle nodes van een stap in zitten.

## 5. Node

```
{ "id": "n_check", "name": "Volledige naam", "spaceId": "space_world",
  "position": { "x": -5, "y": 0, "z": -1 },
  "visuals": { "geometry": "guard", "variant": "auth", "performer": "human", "color": "#ef4444",
               "label": "Max ~22 tekens", "sublabel": "Max ~26 tekens" } }
```

Houd `label` kort; de volledige naam zet je in `name` (die verschijnt als je de node aanwijst). `variant` is optioneel (eerste = standaard). `performer` (optioneel): `human` (mensje) of `system` (robotje) bij het station. Stations (behalve `person`) hebben zelf geen poppetje: zet `performer` als er zichtbaar iemand of iets aan het werk moet zijn (bij een `clerk` vrijwel altijd `human`). `turn` (optioneel, `0`–`3` kwartslagen) draait de voorkant; laat het meestal weg, dan kiest de app de kant van het pad. Gebruik de kleur uit de tabel tenzij er reden is af te wijken. De omschrijvingen zijn uitleg, nooit een waarde.

| `geometry` | `variant` | gebruik voor | kleur |
|---|---|---|---|
| `trigger` | `clock` (schema/cron), `command` (formulier, API-verzoek), `event` (webhook, alarm), `manual` (startknop) | start van de flow | `#10b981` |
| `end` | – | goed einde: klaar, geleverd, betaald | `#16a34a` |
| `reject` | – | fout-einde: afgewezen, mislukt, geannuleerd | `#dc2626` |
| `milestone` | – | belangrijk moment, geen werkstap; vlag gaat omhoog als een actor aankomt | `#f97316` |
| `computer` | `service`, `script`, `ai` | software die werk doet | `#60a5fa` |
| `factory` | – | iets maken of samenstellen | `#f59e0b` |
| `transform` | `convert` (andere vorm), `enrich` (er komt iets bij) | omzetten, formatteren, vertalen | `#06b6d4` |
| `clerk` | – | balie, menselijke/handmatige stap (met `performer` `human`) | `#14b8a6` |
| `commit` | – | definitief maken: boeken, ondertekenen, publiceren | `#be123c` |
| `guard` | `validate`, `auth`, `filter`, `ratelimit` | controle, poortje | `#ef4444` |
| `decision` | `yesno`, `multi`, `balance` (verdelen, router) | waar iets heen gaat; uitgaande paden krijgen een `name`, tijdlijn vertakt | `#6366f1` |
| `merger` | `aggregate`, `join` (wacht op alle takken) | samenvoegen | `#38bdf8` |
| `splitter` | `parts`, `copy` | één item wordt er meerdere | `#0284c7` |
| `queue` | `fifo`, `priority`, `pick` | wachtrij; payloads op deze node staan zichtbaar in de rij | `#d946ef` |
| `wait` | `delay`, `until`, `schedule` | pauze midden in de flow | `#eab308` |
| `lock` | – | beperkte toegang; in `visuals`: `"state"` `open`/`closed`, `"capacity"`, `"inUse"` | `#ca8a04` |
| `storage` | `database`, `log`, `file`, `cache`, `warehouse` | bewaren, voorraad | `#8b5cf6` |
| `board` | `report`, `dashboard`, `screen` | informatie tonen | `#eab308` |
| `mailbox` | `email`, `notification`, `chat` | berichten, afleverpunt | `#f43f5e` |
| `antenna` | `gateway`, `broadcast`, `pubsub` | netwerk, gateway, cloud | `#818cf8` |
| `dock` | `import`, `export`, `upload` | goederen/data in of uit | `#fb923c` |
| `person` | `standing`, `home` (woonhuis) | klant, gebruiker, medewerker | `#ec4899` |
| `external` | `company`, `bank`, `supplier`, `government`, `shop` | externe partij of systeem | `#0ea5e9` |
| `subprocess` | – | stap die zelf een proces is; met `"targetSpaceId"` | `#a855f7` |
| `note` | `hotspot`, `question`, `info` | opmerking naast de flow, geen stap | `#facc15` |
| `block` | – | past nergens anders | `#94a3b8` |

## 6. Path, Actor, Payload

```
{ "id": "p_check_store", "name": "ja", "spaceId": "space_world", "fromNodeId": "n_check", "toNodeId": "n_store",
  "type": "directed", "routing": "straight",
  "visuals": { "color": "#94a3b8", "lineWidth": 3, "style": "solid", "animatedPulse": true } }
```

- `spaceId`: de ruimte van beide nodes, of hun gemeenschappelijke parent. `type`: `directed` | `undirected`. `routing`: altijd `straight` (de app legt zelf een tegelpad met haakse bochten).
- `style`: `solid` (gewone overdracht), `conveyor` (goederen, lopende band), `pipe` (datastroom), `dashed` (informatie; wordt een monorail waar een payload in een karretje overheen rijdt).
- Een pad naar een `reject` (de foutroute) krijgt `"color": "#dc2626"` en `"style": "solid"`.
- Een actor mag een pad terug lopen: zelfde `pathId`, vorige node als `targetNodeId`.

```
{ "id": "actor_main", "name": "Naam", "spaceId": "space_world",
  "visuals": { "shape": "robot", "color": "#38bdf8", "scale": 1, "label": "Korte naam" },
  "state": "idle", "currentPosition": { "x": -11, "y": 0, "z": -1 }, "currentNodeId": "n_start",
  "speed": 3, "carryingPayloadIds": ["pl_order"] }
```

- `shape`: `avatar` (mens), `packet` (datapakket), `robot` (geautomatiseerde agent), `cart` (vrachtwagen), `car` (personenauto), `drone`, `sphere` (abstract signaal). `scale` 0.6–1.2. `currentPosition` = x/z van `currentNodeId`.

```
{ "id": "pl_order", "name": "Naam", "holderType": "actor", "holderId": "actor_main",
  "visuals": { "geometry": "envelope", "color": "#60a5fa", "size": { "x": 0.7, "y": 0.4, "z": 0.7 }, "label": "Korte tekst", "decorations": [] } }
```

- `geometry`: `box` | `envelope` | `token` | `coin` | `data-block` | `sphere`. Afmetingen 0.3–1.2.
- `holderType`: `actor` (staat dan ook in diens `carryingPayloadIds`, en omgekeerd), `node` (ligt bij die node), `space` met `holderId` = hoofdruimte (buiten beeld; verschijnt later via `transfer_payload`).
- Decoration: `{ "type": "header-wrap" | "badge" | "compression-tag", "color": "#hex", "label": "TEKST" }` (laag eromheen / status / eigenschap).

## 7. Tijdlijn

```
{ "id": "kf_1", "time": 1, "description": "Eén zin over wat er gebeurt en waarom.",
  "timeLabel": "Dag 1",                             // optioneel: alleen waar de tijd verspringt
  "action": { "type": "move_actor", "params": { "targetNodeId": "n_check", "pathId": "p_start_check" } },
  "actions": [ ] }                                  // optioneel: extra acties in dezelfde stap
```

Stap N = beginsituatie + alle acties van keyframe 0 t/m N.

- `kf_0` (time 0) is de beginsituatie en wordt bij het openen al toegepast: `{ "type": "custom", "params": {} }`, of een `node_process` die de beginstand zet. Laat er nog niemand in lopen.
- `time` is 0, 1, 2, … oplopend; `duration` = hoogste `time` (ook binnen takken).
- `timeLabel` (optioneel) is de verhaaltijd: "Dag 1", "Die nacht", "3 weken later". Zet het alleen op de stap waar de tijd verspringt; het geldt voor de stappen erna tot het volgende `timeLabel`. Gebruik het als delen van het proces los van elkaar lopen of dagen uit elkaar liggen.
- Een actor loopt alleen over een pad als `pathId` verbindt **de node waar hij nu staat** met `targetNodeId`; anders springt hij.
- Zet bij meer dan één actor altijd `actorId`, bij meer dan één payload altijd `payloadId`.

| `type` | `params` | effect |
|---|---|---|
| `custom` | `{}` of `{ "currentNodeId", "pathId"?, "actorId"? }` | alleen uitleg, eventueel actor verplaatsen |
| `move_actor` | `{ "targetNodeId", "pathId"?, "actorId"? }` | actor loopt naar de node |
| `wrap_payload` | `{ "targetNodeId", "pathId", "decoration", "actorId"?, "payloadId"? }` | lopen en decoratie toevoegen |
| `unwrap_payload` | `{ "targetNodeId", "pathId", "removeDecorationLabel", "actorId"?, "payloadId"? }` | lopen en decoratie met dat label weghalen |
| `transform_payload` | `{ "payloadId", "newVisuals", "targetNodeId"?, "pathId"?, "actorId"? }` | (lopen en) uiterlijk veranderen |
| `transfer_payload` | `{ "payloadId", "toActorId" \| "toNodeId" \| "offStage": true, "targetNodeId"?, "pathId"?, "actorId"?, "newVisuals"? }` | andere houder. Ligt de payload op een node en geef je `toNodeId` + `pathId` (zonder `targetNodeId`), dan reist hij zelf over het pad |
| `send_signal` | `{ "fromNodeId", "toNodeId", "label"?, "color"?, "reply"?, "replyPayloadId"? }` | draadloos contact (bellen, internet, melding, opzoeken): boog met pulsjes, geen pad nodig. `reply` = het antwoord dat terugkomt (lagere boog terug, bijv. "Polis #123, geldig"); `replyPayloadId` = payload die daarbij bij de afzender verschijnt (begint buiten beeld) |
| `node_process` | `{ "nodeId", "label"?, "sublabel"?, "color"?, "variant"?, "state"?, "inUse"?, "capacity"?, "reached"? }` | station doet zichtbaar werk; tekst/kleur/lock-stand/mijlpaalvlag verandert |

- `newVisuals` vervangt per veld; `decorations` vervangt de hele lijst.
- **Parallel**: één actor per tak, alle bewegingen in één keyframe (eerste in `action`, rest in `actions`). Data die splitst: payloads op de splitsnode, elk met `transfer_payload` over een eigen pad.
- **Systeem → mens**: data-payload reist over een `dashed` pad naar het station van de mens; volgende stap gaat die `offStage` en krijgt de mens de fysieke payload (`toActorId`).

### Vertakkingen

Zet `branches` op de keyframe waarin de actor bij de `decision` aankomt. De kijker kiest een tak; zonder keuze speelt de `default`.

```
"branches": [
  { "id": "ja", "label": "Ja", "default": true, "color": "#22c55e", "keyframes": [ Keyframe ] },
  { "id": "nee", "label": "Nee", "color": "#ef4444", "rejoin": false, "keyframes": [ Keyframe ] }
]
```

- Een tak begint bij de `decision`: de eerste beweging gebruikt het pad van de beslissing naar de node van die tak.
- Na een tak gaat de hoofdlijst verder ná de splitsing (takken die samenkomen eindigen op **dezelfde node**), behalve bij `"rejoin": false` (tak stopt, bijv. bij `reject`).
- Twee of meer takken, elk een eigen `color`. Takken mogen zelf weer `branches` hebben. Keyframe-ids zijn uniek over de hele tijdlijn (`kf_ja_1`); `time` telt in een tak door vanaf de splitsing.
- Kan het proces op meer manieren beginnen, vertak dan op de eerste keyframe na `kf_0`.

## 8. Eindcontrole

- Eén ```json-codeblok, geldige JSON, niets vertaald, alleen waarden uit dit document.
- `version` `"1.0"`, alle zeven topniveau-sleutels, één ruimte zonder `parentId` = `activeSpaceId`.
- Randen van ruimtes even; nodes op oneven x/z binnen hun ruimte, één per cel.
- Elke `variant` hoort bij zijn `geometry`; elke verwijzing (`spaceId`, `fromNodeId`, `toNodeId`, `holderId`, `currentNodeId`, `targetNodeId`, `pathId`, `actorId`, `payloadId`, `nodeId`, `targetSpaceId`) bestaat.
- Elke beweging volgt een pad vanaf de huidige node van die actor; paden blijven binnen één niveau.
- Elk niveau heeft minstens 4 nodes; anders hoort het op het niveau erboven.
- `carryingPayloadIds` ↔ `holderType`/`holderId` kloppen; `kf_0` is de beginsituatie; `duration` = hoogste `time`.
- Vraag en antwoord staan in één `send_signal` met `reply`; paden naar een `reject` zijn rood; meerdere ontvangers van één gebeurtenis lopen via een `antenna`, niet via een `splitter`.

## 9. Voorbeeld

```json
{"version":"1.0","meta":{"title":"Factuur goedkeuren","description":"Een factuur komt binnen, wordt gecontroleerd en opgeslagen.","activeSpaceId":"space_world","cameraZoom":22,"gridSize":2},"spaces":{"space_world":{"id":"space_world","name":"Administratie","dimensions":{"width":20,"depth":12,"height":8},"position":{"x":0,"y":0,"z":0},"visuals":{"floorColor":"#1f2330","gridColor":"#3a445c","theme":"minimal"}}},"nodes":{"n_inbox":{"id":"n_inbox","name":"Inbox","spaceId":"space_world","position":{"x":-7,"y":0,"z":1},"visuals":{"geometry":"mailbox","variant":"email","color":"#f43f5e","label":"Inbox","sublabel":"Factuur ontvangen"}},"n_check":{"id":"n_check","name":"Controle","spaceId":"space_world","position":{"x":-1,"y":0,"z":1},"visuals":{"geometry":"guard","variant":"validate","performer":"human","color":"#ef4444","label":"Controle","sublabel":"Bedrag & PO-nummer"}},"n_archive":{"id":"n_archive","name":"Archief","spaceId":"space_world","position":{"x":5,"y":0,"z":1},"visuals":{"geometry":"storage","variant":"file","color":"#8b5cf6","label":"Archief","sublabel":"Boekhouding"}}},"paths":{"p_inbox_check":{"id":"p_inbox_check","spaceId":"space_world","fromNodeId":"n_inbox","toNodeId":"n_check","type":"directed","routing":"straight","visuals":{"color":"#94a3b8","lineWidth":3,"style":"solid","animatedPulse":true}},"p_check_archive":{"id":"p_check_archive","spaceId":"space_world","fromNodeId":"n_check","toNodeId":"n_archive","type":"directed","routing":"straight","visuals":{"color":"#94a3b8","lineWidth":3,"style":"solid","animatedPulse":true}}},"actors":{"actor_clerk":{"id":"actor_clerk","name":"Medewerker","spaceId":"space_world","visuals":{"shape":"avatar","color":"#14b8a6","scale":1,"label":"Medewerker"},"state":"idle","currentPosition":{"x":-7,"y":0,"z":1},"currentNodeId":"n_inbox","speed":3,"carryingPayloadIds":["pl_invoice"]}},"payloads":{"pl_invoice":{"id":"pl_invoice","name":"Factuur","holderType":"actor","holderId":"actor_clerk","visuals":{"geometry":"envelope","color":"#60a5fa","size":{"x":0.7,"y":0.4,"z":0.7},"label":"Factuur €1.250","decorations":[]}}},"timeline":{"currentTime":0,"duration":2,"isPlaying":false,"speed":1,"currentStepIndex":0,"keyframes":[{"id":"kf_0","time":0,"description":"Er komt een factuur van €1.250 binnen in de inbox.","action":{"type":"custom","params":{}}},{"id":"kf_1","time":1,"description":"De medewerker brengt de factuur naar de controle; bedrag en PO-nummer kloppen.","action":{"type":"wrap_payload","params":{"targetNodeId":"n_check","pathId":"p_inbox_check","decoration":{"type":"badge","color":"#10b981","label":"GOEDGEKEURD"}}}},{"id":"kf_2","time":2,"description":"De goedgekeurde factuur wordt in het archief opgeslagen.","action":{"type":"transfer_payload","params":{"targetNodeId":"n_archive","pathId":"p_check_archive","payloadId":"pl_invoice","toNodeId":"n_archive"}},"actions":[{"type":"node_process","params":{"nodeId":"n_archive","sublabel":"+1 factuur"}}]}]}}
```

## 10. Procesbeschrijving

[PLAK HIER JE PROCESBESCHRIJVING]
