Vibe Engineering workflow for Claude Code and Codex. Use for the bare commands ss, qq, or cc; /vibe-harness commands; phase scope; kanban task tracking; work reports; and mandatory post-push review.
Installs into .claude/skills of the current project.
Are you the author of Vibe Harness?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/zest-im-vibe-harness)
---
name: vibe-harness
description: Vibe Engineering workflow for Claude Code and Codex. Use for the bare commands ss, qq, or cc; /vibe-harness commands; phase scope; kanban task tracking; work reports; and mandatory post-push review.
user-invocable: true
---
# Vibe-Engineering — Dev Progress Kanban Board
Track development progress as a kanban board while coding with Claude Code or Codex.
## References — load only when you need them
This file holds the rules you work by. Everything you *look up* lives beside it, so a
session does not pay for reference material it never opens.
| File | Open it when |
|---|---|
| `references/task-schema.md` | Writing or editing a task by hand — field list, required fields on `done`, status values |
| `references/api.md` | Calling the local HTTP API, or explaining the Board UI |
| `references/setup.md` | Installing on a new machine or registering a project |
| `references/worker-runtime.md` | Running the managed worker (lease, heartbeat, test gate) |
| `references/remote-sync.md` | Wiring a project's snapshot to a central dashboard |
## Source of Truth
**The source of truth is the JSON files under each project's `vibe-harness/` directory:**
- `vibe-harness/kanban.json` — tasks (todo, in_progress, review, done)
- `vibe-harness/decisions.json` — durable technical decisions
- `vibe-harness/archive/YYYY-MM.json` — monthly archives of done tasks
The local server at `localhost:4242` is **a Board UI and a convenience wrapper around these files** — not the system of record. Anything Claude or a teammate can do via the API can also be done by editing the JSON directly, and edits land in the same files either way.
This matters because:
- The JSON files are git-tracked and travel with the code.
- The server may not be running. That is not an error condition.
- Direct edits are the most reliable way for an agent to record progress.
## Core Principles
1. **JSON-first**: kanban.json and decisions.json are authoritative; the server is optional.
2. **Multi-project**: A single optional server can host multiple projects via tabs.
3. **Auto-tracking timestamps**: `started_at` on `in_progress`, `completed_at` on `done`.
4. **Code change stats**: `lines_added`, `lines_removed` measured via `git diff`.
5. **Work reports**: `details` field stores per-task reports (changed files, decisions).
6. **Git-friendly**: JSON storage — diffs, merges, and code review all work naturally.
7. **Archive**: Done tasks archived to monthly files, always visible in dashboard.
## File Locations
```
~/.claude/skills/vibe-harness/server.py ← Web server (optional, shared across projects)
~/.claude/skills/vibe-harness/kanban.html ← Web UI (loaded by server)
~/.claude/skills/vibe-harness/projects.json ← Project registry (used by server)
~/.claude/skills/vibe-harness/users.json ← Display-name alias map (optional, machine-local)
~/.claude/skills/vibe-harness/sync.json ← Remote sync config + tokens (0600, machine-local, never commit)
~/.claude/skills/vibe-harness/push-state.json ← Incremental push state (machine-local)
{project}/vibe-harness/kanban.json ← Per-project task data (git-tracked, authoritative)
{project}/vibe-harness/decisions.json ← Per-project decision log (git-tracked, authoritative)
{project}/vibe-harness/archive/YYYY-MM.json ← Monthly archives (git-tracked)
```
## Commands
| Command | Description |
|---|---|
| `/vibe-harness` | Show current status summary |
| `/vibe-harness serve` | Start the optional Board UI server (`localhost:4242/kanban`) |
| `/vibe-harness stop` | Stop server |
| `/vibe-harness add <title>` | Add new task (default: todo) |
| `/vibe-harness start <id or title>` | Move to in_progress |
| `/vibe-harness done <id or title>` | Move to done |
| `/vibe-harness move <id> <status>` | Change status |
| `/vibe-harness update <id>` | Update task details |
| `/vibe-harness list [status]` | List tasks |
| `/vibe-harness sync` | Sync tasks from PROGRESS.md / devlog |
| `/vibe-harness search <query>` | Search tasks, archives, decisions, docs and commit messages — snippets with locators, no server needed |
| `/vibe-harness report` | Today's completed work summary |
| `/vibe-harness export` | Export all tasks to JSON file (`vibe-harness/kanban-export.json`) |
| `/vibe-harness import` | Import tasks from JSON file (merge mode: keeps newer version) |
| `/vibe-harness import replace` | Import tasks from JSON file (replace mode: overwrite all) |
| `/vibe-harness archive` | Archive done tasks to monthly files |
| `/vibe-harness phase` | Show current phase status + task summary per phase |
| `/vibe-harness phase init` | Create PHASES.md + docs/CURRENT_PHASE.md templates |
| `/vibe-harness phase done` | Complete current phase — runs automated pre-check, updates PHASES.md |
| `/vibe-harness phase next <name>` | Transition to next phase (runs checklist first) |
| `/vibe-harness phase check` | Run automated phase completion check (non-destructive) |
| `/vibe-harness decide "<title>"` | Record a technical decision in the Decision Log |
These commands are convenience wrappers. They ultimately read and write the same JSON files an agent would edit directly.
## Token usage collection
Opt-in. Reads Claude Code transcripts (`~/.claude/projects/`) and rebuilds usage per session per
calendar day (KST). Nothing is transmitted unless `sync.json` has an endpoint and a credential.
```bash
python3 scripts/enroll.py --token <t> --machine <name> --runs-schema 2 # register this machine
python3 scripts/enroll.py --add-project <key>=<repo path> # register a project
python3 scripts/enroll.py --repair # re-point after repo move
python3 scripts/reconcile_runs.py --all --dry-run # preview
python3 scripts/reconcile_runs.py --all --push # send changed rows only
python3 scripts/reconcile_runs.py --all --push --force-full-push # resend everything (recovery)
```
Rules every coding agent must follow here:
- **`--add-project` is not optional.** Collection only walks projects in `projects.json`. A machine
with an empty registry used to exit 0 having done nothing; it now fails loudly, but the fix is
still to register the projects.
- **Project keys are shared, paths are not.** Use the same key every machine uses; the repo path
differs per machine. A made-up key produces rows the dashboard never reads.
- **Never reuse a retired project key, and never merge two keys because the names look alike.**
A key is an identity, not a label. Registering a live repository under a dead project's key
mixes two unrelated histories, and once mixed they cannot be told apart. If you are unsure which
key a repository belongs to, ask — do not guess from the folder name.
- **Person totals survive key fragmentation.** Usage is summed per owner across every project, so
a wrong key does not lose a person's numbers; it only splits the per-project view. Fix the key
going forward rather than rewriting history.
- **`secret` and `runs_token` are different credentials.** `secret` signs dashboard snapshots
(per project, shared); `runs_token` signs usage rows (per person). Writing a personal token into
`secret` breaks snapshot sync with a 401.
- **Never commit `sync.json`** or paste a token into chat, a commit message, or an issue.
- Re-running `enroll.py` is safe. The LaunchAgent label and the Windows task name are fixed.
## Shorthand Commands (user types these directly)
Three two-letter shorthands cover the daily rhythm — sync at session start (`ss`), wrap up at day end (`qq`), ship when ready (`cc`). When the user types one of these as a bare message, run the corresponding procedure.
### `ss` — sync & status
Check this repo's remote and pull everything, branches included:
1. `git fetch --all --prune --tags` — refresh remote branches/tags, prune deleted ones.
2. Fast-forward pull the current branch. Fast-forward other local tracking branches too (`git fetch origin <branch>:<branch>`). Never auto-merge a diverged branch — report it instead.
3. Report: current branch + ahead/behind vs remote, remote branch list (flag unmerged ones), uncommitted files (`git status`), and a summary of newly pulled commits if any.
4. If the pull changed harness code (`scripts/`), check whether the installed copies (`~/.claude/skills/vibe-harness/`) and any running server need updating, and tell the user.
5. **Daily review pass** — run the `vibe-review` skill in daily mode over the work since the previous session, and report it as part of `ss`. If nothing has landed since the last pass, say so in one line and move on; that is what keeps repeated `ss` calls cheap. This is the session-start ritual that makes yesterday's gap between claim and reality visible before new work starts.
### `qq` — daily wrap-up (no commit)
1. Print kanban summary (today's done, in-progress, tomorrow's tasks) — derived from kanban.json.
2. Update any in-progress task's `details` with an interim report; write `details` on done tasks that lack them.
3. Update phase docs only when warranted: `PHASES.md` on phase completion, `CURRENT_PHASE.md` on scope / Known Issues change.
4. **Code review** on uncommitted + unpushed changes (see Code Review section).
5. **Archive if `kanban.json` has grown** — more than ~30 done tasks or above ~50KB. See
"Keeping context cheap". Archiving is a routine; skipping it is how a project ends up
paying six figures of tokens to answer "what am I working on".
6. Update `docs/PROGRESS.md` — concise phase-level completion items only. It is an external-sharing snapshot, not a daily log; the day-to-day record stays in `kanban.json`.
7. No commit, no push — `qq` is documentation only.
### `cc` — commit + push (+ deploy check)
1. Everything `qq` does (skip whatever is already clean).
2. `git add` + `git commit` — code and its related docs (kanban.json included) go in ONE commit, never split.
3. `git push origin main`.
4. If the project has CI/CD wired to push, check build/deploy status after pushing and report it. Avoid docs-only pushes on CI/CD-triggering repos.
## Keeping context cheap
A harness that costs thousands of tokens to consult is a harness people stop consulting.
Four rules, learned by measuring rather than guessing:
**1. Measure before optimizing.** Find where the tokens actually go before changing
anything. Measured across 15 projects, `kanban.json` alone accounted for ~946,000 tokens
of session-start cost — one project spent 154,000 tokens just to answer "what am I
working on". That number decided what to fix; intuition would have pointed elsewhere.
**2. Split growing records into hot and archived.** Task history grows without limit but
only the recent edge affects the next decision. Done tasks belong in
`archive/YYYY-MM.json` — still visible on the board and counted in `stats`, but no longer
loaded into every session.
**3. Read a summary; fetch the full record only when the reasoning matters.** A task's
`details` runs to thousands of characters. Titles and change counts answer almost every
session-start question; open the full report when a specific past decision is actually in
question.
**4. Rules live in `SKILL.md`; lookups live in `references/`.** If it is something you
consult while doing a particular job — a field list, an API shape, install steps — it does
not belong in the file loaded on every invocation.
### Search the record before reading it
Archiving made session start cheap by *not reading* history. That only works if the
unread stays reachable — otherwise "why did we decide this?" costs a full load, and the
saving turns into amnesia.
**Reach for search before you open files**, whenever the question is about the past:
- "why is it done this way" · "have we hit this before" · "what did we decide about X"
- before re-litigating a decision, or re-solving a problem that feels familiar
- when a plan mentions something you have no context for
```bash
python3 scripts/search.py 이중 계상 # or ~/.claude/skills/vibe-harness/search.py
python3 scripts/search.py --json --limit 20 coverage
```
It searches tasks, archives, decisions, project markdown **and commit messages** — the
last one matters most, because that is where the reasoning usually ends up. Every hit
comes back with a locator you can open (`docs/PLAN.md:42`, `git show a1b2c3`).
**No server needed.** It reads the JSON and the working tree directly, so it works when
the board UI is not running — which is the normal state.
Three habits that keep it useful:
- **A zero-result search is a claim about the corpus, not about reality.** The response
tells you how many records it scanned; if that number looks wrong, suspect the search
before concluding the thing never happened.
- **Grep is still better for code.** Source files are deliberately outside this corpus —
they would swamp the documents. Use `Grep`/`Explore` for implementation, this for intent.
- **Don't put search results into every session.** It costs only when asked, and that is
the whole reason session start stayed at 867 tokens.
### Archiving is a routine, not a migration
**This is the rule most often missed.** Of the projects measured, several had an
`archive/` directory *and* over 200 done tasks still sitting in `kanban.json` — archived
once, then left to refill.
Archive whenever done tasks accumulate — during `qq` is the natural moment. A practical
trigger: **more than ~30 done tasks in `kanban.json`, or the file above ~50KB.** Run
`/vibe-harness archive` and the month's work moves out in one step.
After archiving, confirm `next_id` still exceeds every archived id. Reusing an id that
only exists in an archive is silent corruption — the board looks fine and two tasks share
a number.
## Session Start (Claude should follow)
At the start of a session, establish current mission context. **Load a summary, not the archive.** A project's task history grows without limit; reading it whole costs thousands of tokens every session and almost none of it changes what you do next.
**Preferred — one call, if the server is already running:**
```
GET http://localhost:4242/api/{project_key}/context
```
Returns phase, scope, checklist, `Do NOT touch`, `in_progress`, recent completions, and stats. Recent completions carry titles and change counts, **not** their full reports — fetch an individual task only when you actually need its reasoning. **Do not start the server just for this**; if it is not up, use the fallback.
**Fallback — read files directly:**
1. **`CURRENT_PHASE.md`** (checked in `private/`, then root, then `docs/`) — phase name, scope, `Done when`, and the `Do NOT touch` block. This is the primary scope signal.
2. **`vibe-harness/kanban.json`** — the active `in_progress` task, if any. Done tasks live in `vibe-harness/archive/YYYY-MM.json`; **do not read the archives at session start.** Open one only when a specific past decision is in question.
3. **`vibe-harness/decisions.json`** — recent durable decisions that may constrain the work.
4. **`docs/planning/01_philosophy.md`** (if present) — the project's north star, written by the `vibe-planning` skill. Its principles and the "who we are not for" section are decision constraints; when a task conflicts with them, say so rather than quietly picking a side.
**Keep `kanban.json` small.** Run `/vibe-harness archive` when done tasks accumulate. The board still shows them — archives are read by the dashboard and by `stats` — but they stop being loaded into every session.
From these, derive:
- Current Phase name and scope
- Existing `in_progress` task (resume) vs. new work
- The `Do NOT touch` list (the scope-guard hook also enforces this — see Phase Management below)
- Open checklist items
If no `CURRENT_PHASE.md` exists anywhere (`private/` / root / `docs/`) but the project already has kanban tasks, the session-start hook nudges you. Run `/vibe-harness phase init` to scaffold it, or ask the user how they want to scope the session before creating new tasks. Don't silently start work without a scope.
### Prerequisites: what the board can and cannot know
`depends_on: [id, ...]` is optional and expresses **one shape only** — this task waits on
that task. When it is set, `/context` reports `blocked`, `ready`, dangling references and
cycles, and those answers are correct.
**Do not force other shapes into it.** Measured across 22 projects: of the active tasks
that state a prerequisite in their own text, **1 could be expressed as task→task and 27
could not** — they wait on activities, phases or external events ("after the G2B import",
"once the merge lands"). Inventing task ids for those makes the board lie.
So the rule is narrow:
- **A task waits on another task, and you know its id** → set `depends_on`. This is the
case `/context` can act on.
- **Anything else** → say it plainly in `details`. That is not a failure to use the
field; it is a prerequisite the field cannot hold.
**Read `dependencies.note` before trusting `blocked`.** An empty `blocked` means one of
two different things, and `/context` now says which: *nothing is blocked*, or *nobody
declared a dependency, so there is nothing to judge*. `stated_in_prose` lists the active
tasks whose text mentions a prerequisite — treat it as a reading aid, not a to-do list.
Most entries there cannot and should not become `depends_on`.
### A working tree you are not alone in
The session-start hook also reports uncommitted changes it finds in the tree. Read that
warning before you touch anything: **at session start, nothing in the tree is yours.** It
is either a previous session's leftovers or a session that is running right now — parallel
agents share one checkout, and the tree does not say who wrote what.
The accident this prevents is specific. `git add -A` does not distinguish, so one session
commits another's in-flight files; that is how internal figures reached a public repo.
- Stage **paths you named**, not `-A` / `.`, whenever the hook warned.
- 🔴 `.git/index.lock` means another git process is running *now*. Wait, don't force it.
- The warning never blocks and never fails a command. It is information, and acting on it
is your job — a check that stops work gets switched off, and then it protects nothing.
---
## ⚠️ Code Review (MANDATORY — EVERY CODING AGENT MUST FOLLOW)
**The coding agent MUST perform a code review immediately after executing `git push` or `gh pr create`.**
Not optional. Don't skip to the next task. Don't ask the user if they want a review. Just do it.
#### Trigger conditions (ANY of these = review MUST happen)
1. **You just ran `git push`** → review NOW
2. **You just ran `gh pr create`** → review NOW
3. **User says `qq`** → review all uncommitted + unpushed changes
4. **User says `/vibe-harness review`** → review on demand
#### Step 1: Get the diff
```bash
# After git push:
git diff @{push}..HEAD 2>/dev/null || git diff origin/$(git branch --show-current)..HEAD
# After gh pr create:
git diff main..HEAD
# After qq:
git diff HEAD
```
#### Step 2: Review the diff — focus on CRITICAL and HIGH only
- 🔴 **CRITICAL**: Security issues (secrets, injection, auth bypass, XSS)
- 🟡 **HIGH**: Bug risks (null refs, race conditions, missing error handling, data loss)
Skip MEDIUM — save those for dedicated refactoring sessions.
#### Step 3: Create review tasks in kanban.json
For each CRITICAL or HIGH finding, append a task to `kanban.json` with:
- `status: "review"`
- `category: "review"`
- `priority: "high"`
- `created_by: "claude-review"`
- Title format: `🔴 SQL injection in search_handler` or `🟡 Race condition in worker pool`
Follow the same `next_id` / timestamp rules as any other task. (If the Board UI server is up you may POST instead — same effect, same file.)
#### Step 4: Print summary
```
📋 Code Review
━━━━━━━━━━━━━━
🔴 CRITICAL: (count)
🟡 HIGH: (count)
━━━━━━━━━━━━━━
Created (N) review tasks in kanban.
```
If clean:
```
📋 Code Review — ✅ No critical/high issues found.
```
#### Step 5: If CRITICAL → warn prominently
```
⚠️ CRITICAL SECURITY ISSUE — fix before merging
→ (description)
```
Resolve review tasks by moving them to `done` once fixed.
### Key Rules
- PROGRESS.md is an "external-sharing snapshot" — updated at `qq` / `cc` with concise phase-level completion items, never a daily log. The day-to-day record lives in kanban.json.
- Every task MUST have a `category` (backend, frontend, infra, data, docs, qa, review, etc.)
- Write `details` thoroughly on completion — viewable in web UI and in git history.
- Record `tokens_used` (estimated integer) on completion.
## Phase Management
Vibe-Engineering includes a built-in phase management system for structured project progression.
### Phase Naming Convention
```
PHASE_{PURPOSE}{NN}
```
| Prefix | Focus | Transition Condition |
|--------|-------|---------------------|
| `SEED` | Initial structure, boilerplate, DB schema | Basic structure + data loading done |
| `MVP` | Core features only — prove it works | Demoable to users |
| `PMF` | User feedback-driven iteration | Real user feedback collection started |
| `SCALE`| Performance, architecture cleanup, AI features | Traffic/data bottleneck detected |
| `GTM` | Launch, marketing integration | Product stabilized |
Example: `PHASE_MVP01`, `PHASE_PMF02`
### Required Files (per project)
**`PHASES.md`** — Master plan (all phases + completion history)
```
## PHASE_MVP01 ✅ DONE (2026-03-01)
> One-line summary
- Completed item 1
- Completed item 2
- N tests
## PHASE_MVP02 🚧 IN PROGRESS
> One-line summary
- [x] Completed item
- [ ] Pending item
## PHASE_PMF01 ⏳ PENDING
> One-line summary
```
**`docs/CURRENT_PHASE.md`** — Current session scope (keep short)
```
## Now: PHASE_MVP02
## Scope: [features/files]
## Done when:
- [ ] Item 1
- [ ] Item 2
## Do NOT touch:
<!-- SCOPE_LOCK_BEGIN -->
- billing/
- auth/
- app/models/user.rb
<!-- SCOPE_LOCK_END -->
```
The `SCOPE_LOCK_BEGIN` / `SCOPE_LOCK_END` block is machine-readable: the `PreToolUse` scope guard hook reads it automatically and **blocks** any Edit or Write to matching files or directories. Patterns are prefix-matched against the relative path from the project root.
### Phase Rules (every coding agent MUST follow)
1. **Read `PHASES.md` and `docs/CURRENT_PHASE.md` at session start.**
2. **Never advance to the next Phase without explicit user instruction.**
3. **Update `PHASES.md` on Phase completion** (date, achievements, test count).
4. **Strictly respect `docs/CURRENT_PHASE.md`'s `Do NOT touch` list.**
5. **Phase completion criteria**: All items in `Done when` checklist must be checked. Partial = 🚧.
### Phase Transition Checklist
| Item | Verify |
|------|--------|
| Tests | All tests for current Phase features pass |
| Docs | `PHASES.md` updated with completion details + test count |
| Tech debt | Any debt carried forward is noted in PENDING Phase |
| Vibe-Engineering | All tasks for the Phase are `done` (check kanban.json) |
The `/vibe-harness phase check` and `/vibe-harness phase done` commands automate this — they read kanban.json and the phase markdown files. If the server is up, the same check is exposed at `GET /api/{project}/phase-check` as a convenience.
### Coding Style per Phase
| Phase | Test Coverage | Refactoring | Logging |
|-------|--------------|-------------|---------|
| SEED | None OK | No | No |
| MVP | Critical paths only | No | Minimal |
| PMF | Feature-level | Light | Feature flags + metrics |
| SCALE | Full | Yes | Full |
## Multi-User Rules
Vibe-Engineering uses JSON files for storage — fully git-friendly.
### Storage — Git-Tracked
- `vibe-harness/kanban.json` — active tasks (todo, in_progress, review + recent done)
- `vibe-harness/decisions.json` — durable decisions
- `vibe-harness/archive/YYYY-MM.json` — monthly archives of completed tasks
- All files are text-based JSON — git diff, merge, and conflict resolution work naturally
- Commit these files along with your code
### Sharing Workflow
```
1. Work normally — kanban.json is updated as you work (direct edits or via API)
2. git add vibe-harness/ → commit with your code changes
3. On pull — if merge conflict in kanban.json, resolve like any JSON (per-task)
4. Archive periodically: /vibe-harness archive → moves done tasks to archive/YYYY-MM.json
```
### User Identification
- Tasks have `created_by` and `assigned_to` fields
- Set `created_by` from `git config user.name` on task creation
- `in_progress` limit (1 task) applies **per user**, not globally
- When starting a task, set `assigned_to` to current user
#### Display-name aliases (`users.json`)
`git config user.name` is often not the handle you want on the board — the same person
shows up as `a.kim` on one machine, `akim-work` on another, and under their display name
when a task is typed by hand. Normalizing that by editing the JSON after the fact does not
hold: the next task created re-introduces the raw git name, so the same sweep runs again.
Canonicalize at the point of entry instead. Create an optional machine-local map:
```json
// ~/.claude/skills/vibe-harness/users.json (chmod 600 — it holds real names)
{
"aliases": {
"a.kim": "akim",
"akim-work": "akim",
"Ayeong Kim": "akim"
}
}
```
- Applied wherever an owner field is written: `_git_user()`, task creation, task update.
- Lookup is case-insensitive and whitespace-trimmed. Unknown names pass through untouched,
so real teammates who need no alias are unaffected.
- The file lives **outside** any repo — real names never land in a public project.
- Missing or malformed file = no aliases. Nothing changes for anyone who skips this.
- Override the path with `VIBE_HARNESS_USERS_CONFIG` (used by the tests).
Set the canonical handle here rather than changing `git config user.name`: that name is
usually global, so changing it rewrites the author on every future commit in every repo.
### Agent Auto-Recording Rules (Multi-User)
1. On task creation → set `created_by` to `git config user.name`
2. On start → set `assigned_to` to current user
3. **On direct-to-done recording** (task created retroactively as `done`) → set BOTH `created_by` and `assigned_to`; the start trigger never fires for these
4. Human fields hold human names only — never write agent names (`claude`, `codex`) into `created_by`/`assigned_to`; agents are attributed via `runs.json` `agent`
5. On `in_progress` enforcement → only move **your own** prior in_progress tasks back to `todo`, never someone else's
6. Periodically archive done tasks (`/vibe-harness archive`) to keep kanban.json small
---
## Notes
- Single optional server serves all projects (port 4242, localhost only)
- `~/.claude/skills/vibe-harness/projects.json` stores the project list used by the server
- Each project's `vibe-harness/` directory should be git-tracked (kanban.json + decisions.json + archive/)
- JSON writes — both server and direct edits — should use atomic file replacement (write to `.tmp`, then rename) for safety