From 344ab3301cdc6ebb00cf0a020e290cfd72a90745 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Fri, 11 Sep 2026 10:11:00 -0400 Subject: [PATCH] slab: the rock takes the name of the piece it is holding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opening a piece called `balozo` handed you a rock called `muzad`. Both names are drawn from the same fourteen consonants and five vowels, so neither looked wrong — they just had nothing to do with each other, and you had to hold two words for one session. They were independent by construction. The piece's slug comes from aesthetic-code's `randomSlug`; the rock's name came from `SigilRenderer.name`, seeded on the Slab session UUID. The marker has carried a `piece` field the whole time and the Swift side simply never read it — `ClaudeSession` parsed `scan_url` out of the same JSON object and stopped one key short. For an Aesthetic Code session the piece now names the rock. That is not an arbitrary tiebreak between two equal names: the slug is already the session's public identity, the thing in the address on the rock and in the channel the piece is pushed on. The rock was the only surface disagreeing. Precedent for an external name winning was already here — Loopboys take theirs off the contact route — so this is a second case of the same rule rather than a new one, and it sits in the same function. Non-Aesthetic-Code sessions are untouched: Claude and Codex rocks have no piece and keep deriving from the session id. The slug is filtered to letters, digits, hyphen and underscore and capped at sixteen characters before it becomes a fleet-visible handle, and an empty result falls through to the old derivation, so a session that has not named a piece yet still gets a rock. The name follows a rename, which the old doc comment warned against — it wanted a handle stable for the session lifetime. That reasoning holds for a prompt whose subject drifts; it does not hold for a piece, where a rename is a deliberate act and having the rock keep the dead name is the worse outcome. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01A1xZ1BonXHyds6EzF5H9Hb --- .../Sources/SlabMenubar/ClaudeSession.swift | 7 +++++++ .../Sources/SlabMenubar/SigilRenderer.swift | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/slab/menubar-swift/Sources/SlabMenubar/ClaudeSession.swift b/slab/menubar-swift/Sources/SlabMenubar/ClaudeSession.swift index 669933661a..26cdd08390 100644 --- a/slab/menubar-swift/Sources/SlabMenubar/ClaudeSession.swift +++ b/slab/menubar-swift/Sources/SlabMenubar/ClaudeSession.swift @@ -105,6 +105,12 @@ struct ClaudeSession { /// The prompt rock turns this into a QR surface; everything else ignores it. var scanURL: String = "" + /// The piece this session is writing, with its extension — `balozo.mjs`. + /// Only Aesthetic Code sets it, and it names the rock: a session that is + /// holding a piece should be addressable by that piece's name rather than + /// by a second unrelated word drawn from its session id. + var piece: String = "" + /// Native provider thread id. Claude uses `sessionId`; Codex's tracked /// wrapper has its own rock id, so the watcher records the rollout id here. var providerSessionId: String = "" @@ -371,6 +377,7 @@ enum ClaudeSessionReader { session.loopboyResponse = (obj["loopboy_response"] as? String) ?? "" session.nudgeScreen = (obj["nudge_screen"] as? String) ?? "" session.scanURL = (obj["scan_url"] as? String) ?? "" + session.piece = (obj["piece"] as? String) ?? "" return session } diff --git a/slab/menubar-swift/Sources/SlabMenubar/SigilRenderer.swift b/slab/menubar-swift/Sources/SlabMenubar/SigilRenderer.swift index 40462f769c..80ef4ad44b 100644 --- a/slab/menubar-swift/Sources/SlabMenubar/SigilRenderer.swift +++ b/slab/menubar-swift/Sources/SlabMenubar/SigilRenderer.swift @@ -70,6 +70,17 @@ enum SigilRenderer { return routeName } } + // An Aesthetic Code session is holding one named piece, and that name is + // already the session's public identity: it is the slug in the address on + // the rock and the channel the piece is pushed on. Drawing a second, + // unrelated word from the session id meant the rock and the piece + // disagreed about what the session was called — you would open `balozo` + // and be handed `muzad`. The piece wins, and follows a rename. + if session.agentType == "aesthetic-code" { + let slug = (session.piece as NSString).deletingPathExtension + let clean = slug.filter { $0.isLetter || $0.isNumber || $0 == "-" || $0 == "_" } + if !clean.isEmpty { return String(clean.prefix(16)) } + } return name(forSessionId: session.sessionId) } -- 2.51.2