| name | vibekanban |
| description | Use when working on the visual card UI, Quick Look previews, Zellij pane display, card glyphs, or the overall kanban UX of the jobcard system. |
Vibekanban
What it is
Vibekanban is the visual UX layer on top of the jobcard system — not a
separate tool. It emerges from three components working together:
- Quick Look —
.jobcard bundles render as dark playing-card UI in Finder
- Zellij — 7-pane pipeline layout + pane-per-card focus navigation
bop CLI — control plane (inspect, poker, status, logs)
Quick Look Extension
macos/bop/ — Swift/SwiftUI QL extension. Key file: PreviewViewController.swift.
QL Sandbox Lessons (hard-won)
- Extension MUST be sandboxed (
com.apple.security.app-sandbox = true)
MACH_O_TYPE = mh_execute (NOT mh_bundle)
ENABLE_DEBUG_DYLIB = NO
GENERATE_INFOPLIST_FILE = YES auto-qualifies class name
- Sandboxed QL CANNOT shell out — use
changes.json for file listing
NSFileCoordinator fallback for reading files in sandbox
- Register:
lsregister -f ~/Applications/JobCardHost.app && pluginkit -a
Card Anatomy (top→bottom)
[glyph 44pt] [title bold] [P2 badge]
[label pills — colored by kind]
[progress bar]
[subtask dots ●○○○]
──────────────────────
[Spec]──[Plan]──[Code]──[QA] ← stage pipeline
[acceptance criteria]
[clock 22h ago] [⬛ Attach] [● Stop]
^running+session ^running
Attach Button (green)
Appears when stages[current_stage].status == "running" AND zellij_session is set.
Opens bop://card/<id>/session → host app routes to zellij attach <session>.
Stop Button (coral)
Appears when stages[current_stage].status == "running".
URL Scheme (bop://)
Host app (macos/JobCardHost/) registers bop:// URL scheme in Info.plist.
| URL | Action |
|---|
bop://card/<id>/session | zellij attach bop-<id> |
bop://card/<id>/logs | Live tail of card logs in terminal |
Routed in JobCardHostApp.swift via .onOpenURL.
Logs Tab
QL preview includes a Logs tab with live-tail link. Clicking opens terminal
with colorized log output via bop logs <id>.
Glyph System
See unicode-glyphs skill for full reference.
suit = team ♠=CLI ♥=Arch ♦=Quality ♣=Platform
rank = priority Ace=P1 Queen=P2 Jack=P3 5=P4
🃏 = wildcard / needs breakdown (joker — U+1F0CF)
🂿 = RED JOKER — DO NOT USE, renders as ◆ (silent misread)
Glyph is set at creation or via bop poker consensus. Never change mid-flight.
Card Back / Reveal Convention
🂠 (U+1F0A0) in filename or UI means unrevealed estimate/card-back state.
- Consensus reveal flips filename prefix to the chosen face card (example
🂻-feat-auth.jobcard).
- Keep
meta.id stable (feat-auth) while filename glyph changes.
Contrast Rule (Playing Card Fonts)
- Playing-card glyphs visually assume a dark backdrop at larger sizes.
- Keep QL card canvas dark; for light host surfaces, wrap glyph in a dark chip/panel.
- There is no reliable "reverse font" Unicode variant for card backs/faces; do inversion at the view layer (background/foreground swap), not by changing codepoint.
Zellij Integration
7-Pane Layout (layouts/bop.kdl)
┌──────────┬──────────────────┬──────────────────┐
│ BOARD │ SPEC │ QA │
│ watch │ what to build │ did it work? │
│ status ├──────────────────┼──────────────────┤
│ │ STDOUT │ STDERR │
│ │ agent output │ agent errors │
├──────────┴──────────────────┴──────────────────┤
│ INSPECTOR — bop inspect <id> │
├─────────────────────────────────────────────────┤
│ SHELL — bop commands (focus/retry/approve) │
└─────────────────────────────────────────────────┘
Launch: zellij --layout layouts/bop.kdl
bop_focus.zsh — Card Navigator
Navigate all Zellij panes to focus on a specific card:
scripts/bop_focus.zsh <card-id>
scripts/bop_focus.zsh --auto
Uses zellij action write-chars to send commands into named panes.
bop_bop.zsh — Session Bootstrap
Goal→card→Zellij session in one command:
scripts/bop_bop.zsh "add auth to the API"
Budget-aware: reads stage_budgets from template meta.json.
Meta Fields for Zellij
{
"zellij_session": "bop-feat-auth",
"zellij_pane": "3"
}
Set by dispatcher on spawn. Used by QL Attach button and bop_focus.
Display Tiers (by running card count)
- 0–5 cards: per-card pane with glyph in title
- 6–20: team-level pane
- 21+: status bar only
Log Colorization
bop logs <id> outputs tailspin-style colorized logs:
- Timestamps → dim
- ERROR/WARN → red/yellow
- File paths → cyan
- Numbers → magenta
Implemented in colorize_log_line() in main.rs. Detects TTY for auto-disable.
Label Pill Colors
| kind | color |
|---|
| domain | purple |
| effort | gold |
| scope | teal |
| (other) | purple |
Thumbnail Pipeline (macOS)
- Preview image:
<card>.jobcard/QuickLook/Thumbnail.png
- Renderer:
scripts/render_card_thumbnail.swift <meta.json> <out.png>
- Dispatcher/merge-gate refresh on state transitions
- Also refresh after poker-driven filename glyph rename (
🂠 → face card)
- Batch:
scripts/macos_cards_maintenance.zsh [--compress]
Finder Icon Coloring
Cards show their stage as icon color in Finder:
| State | Color |
|---|
| pending | blue |
| running | orange |
| done | green |
| failed | red |
Driven by scripts/set_card_icon.swift. The bop icons FSEvents watcher auto-triggers icon updates on state transitions. Bundled into the bop factory lifecycle.
changes.json (merge-gate artifact)
Written at merge time. QL reads this instead of shelling out to git.
{
"branch": "job/feat-auth",
"files_changed": [
{"path": "src/auth.rs", "status": "added"},
{"path": "src/main.rs", "status": "modified"}
],
"stats": {"files_changed": 2, "insertions": 145, "deletions": 12}
}
Stage Display Names
Never use .capitalized — "QA" → "Qa". Use lookup:
let stageOrder = [("spec","Spec"),("plan","Plan"),("implement","Code"),("qa","QA")]
Render Test (no QL daemon)
swift scripts/render_card_thumbnail.swift <meta.json> <out.png>