Pitfall Persistence dakka

Boy Krump Does Not Unregister

pitfalldakkawebsocket-protocolKNOWLEDGE

What Happened

While writing e2e/helpers.ts::dismissAllBoys for Playwright setup, the first cut sent boy:krump for every boy in warband:init and waited for boy:removed. The acks never arrived: the helper timed out, and warband:init on the next connection still listed the same boys. The symptom is easy to misread as “the WS send didn’t work,” but the server had processed the messages fine; it just didn’t interpret krump the way the name implies.

Root Cause

Two different handlers in crates/server/src/server.rs:

MorkMessage::BoyKrump { boy_id } => {
    let _ = nob.send_event(&boy_id, BoyEvent::Krump).await;  // state transition
    let _ = pty_manager.kill(&boy_id).await;                 // kill child process
    // boy STAYS registered in nob; rebroadcast snapshot with state=krumped
    if let Ok(snapshot) = nob.get_snapshot(&boy_id).await {
        ws_gateway.broadcast_json(&GorkMessage::BoySnapshot { boy_id, snapshot }).await;
    }
}

MorkMessage::BoyDismiss { boy_id } => {
    let _ = pty_manager.kill(&boy_id).await;
    let _ = nob.unregister(&boy_id).await;                   // REMOVE from warband
    ws_gateway.broadcast_json(&GorkMessage::BoyRemoved { boy_id }).await;
}

krump is a theming word for “kill/terminate” (“Krump ‘em, boyz!”: the orkish vocabulary wrapped around dakka’s state machine). It is the Orky word for a state transition: run the Krump event through the nob’s state machine, reach the krumped terminal state, leave the boy in place so the user can see its final status. dismiss is the separate, less-thematic action that actually removes the boy from the warband.

The UI dismiss button (.agent-card-dismiss in the mascot bar, the × in each agent card) calls handleDismiss(boy.id) which correctly sends boy:dismiss. The mistake is easy to make when reading the MorkMessage enum fresh and seeing BoyKrump next to BoyDismiss without the theming context.

Fix

Use boy:dismiss whenever you want the boy to actually go away:

ws.send(JSON.stringify({ type: 'boy:dismiss', boy_id: id }));
// wait for matching `boy:removed` ack

Use boy:krump only when you want the boy to stay registered in a krumped state for user inspection (the “dead but still on the board” pose).

  • [experiments/dakka/2026-04-23-playwright-e2e-specs](/experiments/dakka/2026-04-23-playwright-e2e-specs): the session that surfaced this.
  • [projects/dakka/CLAUDE.md](/projects/dakka/CLAUDE.md): WebSocket protocol and MorkMessage variants.