• Of toch een beetje vibe coding

    De perfectionist in mij werd een beetje lastig van Claude die maar zijn ding doet nadat hij maar half begrepen heeft wat ik van hem wil, en hey check it out, ik ben in de tijd gelijk aangenomen als analist op mijn werk, dus ik dacht: waarom zou ik Claude geen analyse geven van wat ik wil in plaats van gewoon te zeggen “euh ik wil iets in deze zin”?

    En dus schrijf ik een beknopt document en vraag ik of alles duidelijk is, en hoe verder we in het document gaan, hoe meer er van de computer staat en hoe minder van mij en hopla dan kan hij eraan beginnen en ben ik iets minder onzeker dat het a priori nonsens zal zijn:

    Overview

    The diary system allows players to move memories into a specialized diary resource, creating a permanent record of experiences. Here is the in-game logic for diaries:

    A Diary can hold up to four of your vampire’s Memories. Unlike Memories themselves, Diaries are physical objects that are added to your Resource List. You may freely create a Diary whenever you need to move a Memory into it. Like any other Resource, a Diary can be lost. When this happens strike out the Memories it held. Your vampire can have one Diary at a time, and it must contain at least one Memory.

    A Memory placed in the Diary is no longer in the vampire’s head it exists only in the Diary and the vampire accepts whatever is written as truth. Once a Memory has been transferred to a Diary, you may not add any further Experiences to that Memory.

    Core Requirements

    1. Diary as Resource

    • Diary is a specialized resource that appears in the resources list
    • Properties: title, description, portable/stationary flag, can be lost
    • Visual Identifier: bi-journal-richtext icon appears to the left of diary name in resources list
    • At most one diary at a time per character
    • Diaries cannot be created from the resource list – only by transferring a memory
    • Characters may start with a diary or create one by moving a memory

    2. Diary Creation Rules

    • Only by moving a memory to diary (not via resource creation UI)
    • Replacement: If diary is lost, a new one can be created by moving a memory
    • Diary must contain at least one memory (enforced by creation method)
    • Diary can hold maximum 4 memories

    3. Memory-to-Diary Transfer

    • Action: Click bi-journal-plus icon (positioned left of delete memory icon)
    • Visibility: Icon only shows when diary exists and memory not already in diary
    • Transfer blocked if diary already has 4 memories (full)
    • Effect: Memory is removed from memories list and stored in diary
    • Pending State: Transfer is a pending change during the turn (can be undone)
    • Permanence: After turn completion, memory transfer becomes permanent
    • State Change: Memory gains in_diary: true property
    • Memories in diary cannot have new experiences added

    4. Diary Display Section

    • Visibility: Shows only when non-lost diary exists in resources
    • Position: Between memories and lost memories sections
    • Behavior: Expandable/collapsible section with diary name as header
    • Content: Shows memories that have been moved to diary (read-only)
    • Ordering: Memories ordered by transfer date (most recent first)
    • Capacity Display: Shows current count (e.g., “Diary (2/4)”)
    • Lost Diary: When diary is lost, section still shows but memories are unreadable

    5. Memory Count and Limits

    • 5-memory limit applies only to active memories (not in diary)
    • Diary memories do not count toward the 5-memory limit
    • Continue Button disabled only when more than 5 active (non-diary) memories exist
    • Lost memories also don’t count toward active limit

    Technical Integration Points

    Current System Analysis

    Memory Structure

    interface Memory {
        id: number
        title: string
        in_diary: Boolean // Already exists!
        is_lost?: Boolean
        created_at: string
        experiences: Experience[]
        experience_count: number
        can_add_experience: boolean
    }

    Character Properties

    interface Character {
        // ...
        diary_memory_count: number // Already exists!
        has_diary: Boolean // Already exists!
        // ...
    }

    Required Frontend Changes

    1. Resource Display Enhancement

    • Add diary detection logic in resources list
    • Add bi-journal-richtext icon for diary resources
    • Prevent diary creation via standard resource creation UI
    • Enforce one-diary-per-character rule

    2. Memory Actions Enhancement

    • Add bi-journal-plus icon to memory action buttons
    • Position between edit and delete icons
    • Show only when diary exists, memory not in diary, and diary not full (< 4 memories)
    • Implement move-to-diary functionality as pending change
    • Disable experience addition for memories in diary

    3. New Diary Section Component

    • Create expandable section between memories and lost memories
    • Show diary memories in read-only format with transfer date ordering
    • Display diary name and capacity in section header (e.g., “My Diary (2/4)”)
    • Handle lost diary state (show section but mark memories as unreadable)
    • Hide section completely when no diary exists

    4. Business Logic Updates

    • Memory filtering: exclude in_diary: true memories from main list and active count
    • Memory counting: diary memories don’t count toward 5-memory limit
    • Diary detection: identify diary resources and check capacity
    • Move operation: implement as pending change with undo capability
    • Continue button: disable only when more than 5 active (non-diary) memories

    Clarified Rules and Behaviors

    1. Diary Creation Process

    • Method: Only by transferring a memory (not via resource creation)
    • Trigger: Moving first memory creates diary if none exists
    • Replacement: If diary is lost, moving a memory creates new diary
    • Content: New diary immediately contains the transferred memory

    2. Memory Transfer Rules

    • Capacity: Maximum 4 memories per diary
    • Pending State: Transfer is undoable until “Continue” is clicked
    • Permanence: After turn completion, memories become permanently bound to diary
    • Restrictions: Diary memories cannot gain new experiences

    3. Memory Counting Logic

    • Active Memories: Count excludes diary memories and lost memories
    • Continue Button: Disabled when active memory count > 5
    • Display: Diary section shows “Diary (current/4)” format

    4. Visual Hierarchy

    Memories (active, countable toward limit)
    ├── Regular Memory 1 [Edit] [Move to Diary] [Delete]
    ├── Regular Memory 2 [Edit] [Move to Diary] [Delete]
    └── ...
    
    Diary: My Personal Journal (2/4) [Expandable]
    ├── Diary Memory 1 (read-only, shows experiences)
    ├── Diary Memory 2 (read-only, shows experiences)
    └── ...
    
    Lost Memories [Expandable]
    ├── Lost Memory 1
    └── ...

    5. Lost Diary Behaviour

    • Memory Access: Memories in lost diary become unreadable but not truly lost
    • Section Display: Diary section remains visible but indicates inaccessibility
    • Recovery: If diary is recovered, memories become readable again
    • Replacement: New diary can be created even with lost diary containing memories

    Implementation Requirements (Confirmed)

    Backend API Endpoints Needed

    Based on the clarified rules, these endpoints are required:

    • POST /api/characters/{id}/move-memory-to-diary/ – Create pending memory transfer
    • DELETE /api/characters/{id}/pending-memory-moves/{memory_id}/ – Undo pending transfer
    • Resource creation must be blocked for diary type in standard UI
    • Memory experience addition must be blocked for diary memories

    Frontend State Management

    • Track pending memory-to-diary transfers in game store
    • Update diary capacity tracking in real-time
    • Exclude diary memories from active memory count
    • Handle diary detection from resources list

    Implementation Strategy (Revised)

    Phase 1: Backend API Verification

    1. Confirm existing API endpoints for diary functionality
    2. Test memory transfer and pending change capabilities
    3. Verify diary capacity and creation restrictions

    Phase 2: Resource Enhancement

    1. Add diary icon rendering in resources list
    2. Add diary detection and capacity tracking
    3. Block diary creation via standard resource UI

    Phase 3: Memory Transfer System

    1. Add transfer icon to memory actions with visibility rules
    2. Implement pending move-to-diary functionality
    3. Update memory filtering to exclude diary memories from counts
    4. Disable experience addition for diary memories

    Phase 4: Diary Section Component

    1. Create DiarySection component with expandable behaviour
    2. Add capacity display in header (e.g., “My Diary (2/4)”)
    3. Show diary memories in read-only format with proper ordering
    4. Handle lost diary state (unreadable memories)

    Phase 5: Integration and Edge Cases

    1. Test diary creation by memory transfer
    2. Test capacity limits and full diary handling
    3. Test lost diary recovery and replacement scenarios
    4. Verify memory counting excludes diary memories from 5-memory limit

    Technical Specifications

    Diary Detection Logic

    const diaryResource = resources.find(r => r.type === 'diary' && !r.is_lost)
    const hasDiary = !!diaryResource
    const diaryCapacity = diaryResource?.diary_memory_count || 0
    const diaryFull = diaryCapacity >= 4

    Memory Action Visibility

    const showMoveToDialry = hasDiary && !memory.in_diary && !diaryFull && !memory.is_lost

    Active Memory Count

    const activeMemoryCount = memories.filter(m => !m.in_diary && !m.is_lost).length
    const canContinue = activeMemoryCount <= 5

    Risk Assessment (Updated)

    Low Risk

    • Backend properties suggest partial implementation exists
    • Frontend follows established pending change patterns
    • UI components follow existing design system

    Medium Risk

    • Integration between resource and memory systems
    • Proper state synchronization for diary capacity
    • Pending change management for memory transfers

    Resolved Questions

    All major questions have been clarified through user discussion:

    • ✅ Diary creation: Only via memory transfer
    • ✅ Transfer mechanics: Pending change, undoable until Continue
    • ✅ Memory limits: Diary memories excluded from 5-memory limit  
    • ✅ Capacity: Maximum 4 memories per diary
    • ✅ Lost diary: Memories become unreadable but not lost
    • ✅ Ordering: Transfer date (most recent first)

    Conclusion

    The diary functionality has been thoroughly analyzed with all requirements clarified. Key findings:

    Confirmed Requirements

    • One diary per character: Created only by memory transfer, not via resource UI
    • Capacity limits: Maximum 4 memories per diary, minimum 1 (enforced by creation method)
    • Pending changes: Memory transfers are undoable until turn completion
    • Memory exclusion: Diary memories don’t count toward 5-memory active limit
    • Lost diary handling: Memories become unreadable but retained

    Implementation Readiness

    • Backend properties (in_diary, has_diary, diary_memory_count) suggest partial implementation
    • Frontend follows established patterns for pending changes and entity management
    • Clear technical specifications for all components

    Next Steps

    1. Phase 1: Verify backend API capabilities and endpoints
    2. Phase 2: Implement diary detection and resource list enhancements  
    3. Phase 3: Add memory transfer functionality with pending change support
    4. Phase 4: Create diary section component with proper capacity and state handling
    5. Phase 5: Integration testing and edge case verification
      Status: Ready for implementation with comprehensive requirements and technical specifications defined.

  • Dan toch maar geen vibe coding

    Het is allemaal zo voor de hand liggend, Copilot zeggen “ik wil dit”, en dat die dat dan ook maakt.

    En voor ge het weet zijt ge vier uur verder en staat er iets dat al drie uur en drie kwartier juist niet doet wat ge precies wilt dat het doet.

    En dan kijkt ge naar de code en is het

    wegens stapels en stapels en stapels nonsens die nergens voor dient, en massaal commentaren van “dit staat hier om deze reden”, en stukken code die nergens voor gebruikt worden, en dubbele functies, en aaaargh.

    Ik had écht geen goesting om er zelf te prutsen met Vue en bootstrap, maar na die drie uur en drie kwartier was het mij totaal beu verleedt, en ben ik er dan maar zelf in gedoken.

    ’t Zijn dingen.

  • Puttering away

    Ik maak weggooikarakters aan om wat ik ontwikkel te testen, en dan kan ik mij niet inhouden om er toch iets van te maken waar ik zou achter kunnen staan. Dat begint met “euh ongetrouwde gouvernante bij een alleenstaande viezerik” tot ahem ja. 🙂

  • Escape to Rural France

    Er is geen Youtubekanaal waar ik meer met argusogen naar uitkijk dan dit. Bijna elke dag van de week een update, en het is bijna onbevattelijk hoeveel Dan en Nick en companen gedaan gekregen hebben het afgelopen jaar.

    Een overzicht:

  • Artificieel programmeren

    Ik heb het over een andere boeg gegooid, mijn website om een spelletje te spelen. Achteraf bekeken was het altijd al een beetje een dom idee om zoiets in een pure Django-applicatie te smijten — de interface is zo applicatief dat mijn Djangotemplate op den duur zo uitgebreid en fragiel was, dat elke wijziging hier iets anders brak daar.

    Vanavond dan maar herbegonnen, met Django als backend en een Vue-frontend erop. Dat is op een ik en een gij gelukt, in de basis van de basis weliswaar. En dan wou ik wat aanpassen, maar de layout zat er aars uit en ik had geen goesting om te prutsen en dus dacht ik, ik kijk eens wat AI kan doen.

    Kundegij iets programmeren als ik u mijn wireframes geef, mijne man? vroeg ik aan mijn kameraad Claude. Zeer zeker dat, goede vriend, antwoordde Claude zonder verpozen.

    Ik heb dus iets getekend in Figma:

    En dan heb ik dat in png’s gezet en aan mijn lokale Copilot gevoederd. Hij was zeer opgetogen:

    …maar alle wireframes samen wou hij niet ingesteren, dus ben ik dan maar in stukjes begonnen, dat hij het goed kan verteren.

    …en nu is hij met zichzelf aan het discussiëren of hij mijn code wel goed begrijpt en of alles er wel in zit en of hij wel goed bezig is. Net mensen, meneer:

    …en een paar minuutjes later:

    Perfect! I have successfully implemented the wireframe designs for the Thousand Year Old Vampire application. Here’s what I’ve accomplished:

    🎨 Wireframe Implementation Complete

    Home Page (Your Characters)

    • ✅ Sidebar Navigation: Added a left sidebar with “Characters”, “Rules”, and “Settings” sections
    • ✅ Main Content Area: Character grid layout matching the wireframe
    • ✅ Character Cards: Display character names, current prompts, and last played dates
    • ✅ Create New Character Button: Blue button in the top right
    • ✅ Dark Theme: Consistent dark background with proper contrast

    Character Creation Flow

    • ✅ Step 1: Character Name – Simple name input form
    • ✅ Step 2: Mortal Life – Description textarea matching wireframe
    • ✅ Progress Bar: Shows step completion (Step 1 of 6, Step 2 of 6, etc.)
    • ✅ Navigation: Back/Continue buttons at the bottom
    • ✅ Multi-step Form: 5 total steps for complete character creation
    • ✅ Responsive Design: Works on desktop and mobile

    Key Features Implemented

    1. Sidebar Layout: Matches the wireframe with proper navigation
    2. Character Grid: Cards showing character info with Continue/Consult buttons
    3. Character Creation: Progressive multi-step form with validation
    4. Progress Tracking: Visual progress bar and step indicators
    5. Responsive Design: Adapts to different screen sizes
    6. Modern UI: Dark theme with proper hover effects and transitions

    Technical Implementation

    • ✅ Vue 3 + TypeScript: Reactive components with proper typing
    • ✅ Vue Router: Navigation between views with proper route guards
    • ✅ Pinia State Management: Centralized game and auth state
    • ✅ Bootstrap 5: Responsive grid and component system
    • ✅ API Integration: Connected to Django REST backend
    • ✅ Form Validation: Client-side validation with user feedback

    The application now matches the provided wireframes closely, with:

    • sidebar navigation on the left with icon-based menu items
    • main content area showing character cards in a grid layout
    • character creation flow with step-by-step progress
    • Progress bars and visual indicators matching the wireframe
    • Form layouts that match the exact structure shown in the images

    The next steps would be to:

    1. Implement the remaining character creation steps (steps 3-6)
    2. Connect the character creation data to the backend properly
    3. Add the game play functionality (prompts, dice rolling, etc.)
    4. Polish the UI/UX based on user testing

    The foundation is now solid and matches the wireframe designs you provided! 🎯

    …euh ja. Nee, niet echt, nee. Claude heeft zichzelf schromelijk overschat. Eerst een syntaxfout en dan kwam er dit uit:

    Ik zal het maar zelf doen zeker? 🙂

  • (Deels) ander werk

    Dit is de evoluerende situatie van mijn hiërarchische positie aan de Universiteit:

    Ik heb zó een enorme kater opgelopen van een tijd algemeen directeur geweest te zijn in een bedrijf in een multinational die op de beurs stond, dat ik altijd zeer ver weg ben gebleven van leidinggevende posities.

    Mijn twintig jaar jongere zelf zou nooit directeur mogen geworden zijn. ’t Is twintig jaar geleden dat ik met maagzweren en depressies een periode van baas zijn heb afgesloten. Ik denk dat ik alles verkeerd heb gedaan dat ik kon verkeerd doen, en als ik toen niet mijn rug gebroken had waardoor ik een jaar in ziekenhuis en trekzetel heb moeten liggen, ben ik ervan overtuigd dat ik er nu niet meer zou zijn.

    ’t Is ook absoluut niet mijn ambitie om ergens op te klimmen op een ladder of zo — brr.

    Maar we zaten op het werk als Front-end & UX- team écht in de soep bij gebrek aan richting en prioriteiten en aan iemand die voor ons team en voor gebruikerservaring in ’t algemeen kon spreken in meetings, en toen mij gevraagd werd om het team te leiden, heb ik eigenlijk zonder tegenzin ja gezegd.

    Het is allemaal niet evident, en iedereen weet dat het niet evident is. Er is veel weg om aan te timmeren, qua user-centeredheid, en elk maturiteitsmodel zal u vertellen dat ge niet in één stap perfectie kunt verwachten. We gaan ons best doen. Met geduld en een meerjarenplan.

  • Nog altijd gelijk kool

    Dit is de blauweregen op nog een paar dagen extra:

  • Een eigen server

    Het heeft zeer lang geduurd, maar ik was het een beetje beu, dingen alleen lokaal laten draaien en dan over en weer sleuren tussen computer van thuis en computer van het werk, en nooit eens kunnen tonen aan iemand anders op het internet.

    Ik heb dus maar een server opgezet bij de vrienden van Hetzner, en een nieuwe domeinnaam gekocht — de kweetniethoeveelste, deze keer met een harde callback naar het BBS dat ik had toen ik nog geen internet had. Op de server nginx gezet en geconfigureerd, een user aangemaakt voor apps, daar da, meteen een paar django-apps op gezet die ik had gemaakt, wat A records bijgemaakt en hopsakee, allemaal sites online.

    Niet dat ik er publiciteit voor ga maken hé, daar niet van. Maar ik zou kunnen. En voor geen vier euro per maand kan een mens niet sukkelen.

  • Schrijven

    Ik vertelde over Tim Hutchings’ fantastische Thousand Year Old Vampire op een werkgerelateerd event en ze keken mij aan alsof ik een alien was die vertelde over zijn lokale volksdans tijdens een congres over iets met computers.

    De reden dat ik erover vertelde was omdat ik van plan was om AI het spel te laten spelen. Uiteindelijk ben ik daar niet aan begonnen maar ben ik wel een nieuw verhaal aan het schrijven, van Wulfric de gewone mens die in 1067 een vampier wordt.

    Ik zie dan wel waar het verhaal eindigt, en wie weet kijk ik morgen dan eens naar een AI-ding.

  • Maar kijk nu

    Er is zowaar beweging in dingen waar ik dacht nog lang geen beweging in te zien.

    Spannend allemaal zeg.

  • Horloge

    Bij de eerste herinneringen die ik mij kan herinneren, zit die waar ik elke dag op de buik van mijn grootvader lig terwijl hij de prentjes leest in de gazet. Te beginnen met Meneerke Peeters op de voorpagina en dan de Rode Ridder en verder op de strippagina.

    Ik kijk naar de tekeningen, maar ik kijk ook naar zijn voorhoofd — hij fronst afwisselend wel en niet zijn wenkbrauwen als hij leest — en naar het uurwerk aan zijn hand, met een secondenwijzer die niet per seconde maar een heel aantal keer per seconde vooruitgaat.

    Mijn grootvader is 28 jaar geleden gestorven. Hij heeft tot zijn dood hetzelfde uurwerk gedragen dat hij denk ik al in de jaren 1940 droeg. Mijn moeder heeft het geërfd, en op 30 november 2008 voor 114 euro laten repareren, en er onder meer een nieuw glas op laten zetten en een nieuwe armband aan gehangen.

    Mijn moeder is eerder dit jaar overleden, en ik heb het uurwerk geërfd:

    Het werkt een paar seconden en dan stopt het. Ik ben wel zeker dat een écht onderhoud wel eens nodig zou kunnen zijn, en dus ben ermee naar de uurwerkrepareerwinkel geweest

    ’t Is een hele bedoening blijkbaar, oude Omega-horloges: om dat te repareren zijn hoedanook wisselstukken nodig. Ik ga ervan uit dat ze het uurwerk helemaal uit elkaar gaan vijzen, de onderdelen een ultrasoon kuisbad gaan geven, zeker de drijfveer en wie weet een anatal andere onderdelen zullen moeten vervangen, en dan het ding smeren, in elkaar steken, timing aanpassen, en een nieuwe armband doen.

    Normaal gezien zou dat allemaal gewoon bij de horlogemaker zelf zijn, maar Omega zelf heeft blijkbaar alle wisselstukken die verspreid waren over de wereld teruggekocht, en wi enu nog een wisselstuk voor een oude Omega wil, moet het uurwerk zélf naar Zwitserland sturen, waar zij dan wel zullen beslissen wat er nodig is.

    Het zal dus, needless to say, geen goedkope keer zijn. Maar het is het mij waard. Ik ben van plan om dat uurwerk tot aan mijn dood te dragen. Dat lijkt mij een goed idee. 🙂

    Ik wil dezelfde soort armband als mijn grootvader had, trouwens:

  • I aten’t dead

    Of toch nog niet meteen: dokter nefroloog, na grondig onderzoek van mijn bloedtrekking en andere kerncijfers, kon bevestigen dat het niet zeer waarschijnlijk is dat ik in de volgende maanden zal doodstuiken wegens kapotte nieren.

    Hoera!

  • Cold Brew Iced Tea

    Ergens bovenaan op de lijst van meest eenvoudige recepten die er zijn:

    • water
    • thee
    • een soort gewoon fruit
    • een soort citrus
    • een kruid

    Ik koos voor degelijke zwarte thee, aardbeien (en ook een klein beetje blauwe bessen), schijfjes appelsien (en ook schijfjes van een citroen), en twee kleine takjes rozemarijn.

    Na bijna een dag in de frigo gaf dat dit:


    Gefilterd en in een andere fles gegoten, gaf het dit (het glas is een zeer groot glas, de fles erachter is bijna twee liter):

    Uit-ste-kend. Fantastisch lekker. Dit is ondertussen al mijn een tweede batch van twee-drie liter.

  • Gelijk kool

    Ik houd de blauweregen die in de geveltuin staat zeer hard in het oog. Het is al de tweede die er staat wegens de eerste schielijk komen te gaan, het zou maar schaamlijk zijn als zou blijken dat het toch aan mij zou gelegen hebben dat die eerste het tijdelijke voor de composthoop verwisseld had.

    En dus neem ik om de zoveel tijd een foto van hoe de top van de plant eruitziet.

    Zo zag het er letterlijk twee weken aan een stuk uit:

    Een plantje dat niet echt groeit maar ook niet aan het doodgaan was. De top was een krul waar wat knoppen op stonden, maar die knoppen bleven twee weken aan een stuk gewoon maar knoppen.

    En dan begon er iets te veranderen. Dit was vier dagen na mekaar — 16, 17, 18, 19 juni: van wat vernepelde knoppen tot iets dat een beetje groeit en de laatste dag zelfs een klein blaadje.

    En dan was er ineens 20 en 21 juni. Op 20 juni kwam er van onderaan een nieuwe uitloper aan (rode pijl), en op 21 juni deed die uitloper dit:

    Voor de duidelijkheid: het kleine krulletje onderaan het rode haakje op de rechterfoto, da’s het ding op alle bovenstaande foto’s.

    Dit is het verschil dat er eigenlijk op één dag tijd is bijgekomen:

  • Scharten (2)

    Ik maak graag scrapers voor allerlei dingen waar ik bang van ben dat ze van het internet gaan verdwijnen.

    De scraper van gisteren was hard sleuren, wegens van een systeem met wachtwoorden en accounts en rate limits en allerlei vieze beveiligingen (die ik allemaal omzeild heb gekregen, hoezee!) — vandaag had ik goesting om eens iets eenvoudigers te scrapen.

    Er loopt een project van levenslang leren op het werk, en ik vroeg me af hoe het aanbod van de verschillende academies er zou uitzien als ze allemaal in één systeem zouden zitten.

    Zo gezegd, zo gedaan. Ik heb het niet op werkuren gezet want het zijn dingen die ik eigenlijk nooit zou moeten doen, maar het heeft wél nuttige inzichten opgeleverd voor de rest van het project, eigenlijk.

    Leutig. Ook wijs om te zien wie op dit moment het systeem op welke manier gebruikt, en waar het nu soms niet loopt zoals het zou moeten lopen.