# Vibekanban

> 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.

- Skill: `ryanmaclean/vibekanban` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ryanmaclean/vibekanban`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ryanmaclean/vibekanban/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ryanmaclean (https://skillmd.com/u/ryanmaclean)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ryanmaclean/vibekanban

---


# 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:

1. **Quick Look** — `.jobcard` bundles render as dark playing-card UI in Finder
2. **Zellij** — 7-pane pipeline layout + pane-per-card focus navigation
3. **`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:

```zsh
scripts/bop_focus.zsh <card-id>       # populate all panes for card
scripts/bop_focus.zsh --auto          # sweep all panes via zellij action write-chars
```

Uses `zellij action write-chars` to send commands into named panes.

### bop_bop.zsh — Session Bootstrap

Goal→card→Zellij session in one command:

```zsh
scripts/bop_bop.zsh "add auth to the API"
# Creates card: bop-add-auth-to-the-api
# Writes goal to spec.md
# Starts/attaches Zellij session: bop-bop-add-auth-to-the-api
# Runs dispatcher inside session
```

Budget-aware: reads `stage_budgets` from template meta.json.

### Meta Fields for Zellij

```json
{
  "zellij_session": "bop-feat-auth",  // session name
  "zellij_pane": "3"                  // pane ID within session
}
```

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.

```json
{
  "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:
```swift
let stageOrder = [("spec","Spec"),("plan","Plan"),("implement","Code"),("qa","QA")]
```

## Render Test (no QL daemon)

```bash
swift scripts/render_card_thumbnail.swift <meta.json> <out.png>
```

