From builders-dont-cry
Session wrap-up — produces a HANDOFF file at `${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.md` for the next session's AI. Format spec: `standards/ai-friendly-artifacts.md` § 1.4.2. Pending matters more than Done. Use when builder says handoff / 收尾 / 总结一下 / 交接 / 交代一下 / wrap up / session 结束, or PreCompact hook prompts. Skip pure Q&A, mid-execution unstable state, or if the same target handoff was already written within 5 min (per Phase 1.5 recency guard).
How this skill is triggered — by the user, by Claude, or both
Slash command
/builders-dont-cry:handoffThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
One invocation produces:
One invocation produces:
${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.md, per § 1.4.2 of the canonical standard. Every file MUST carry kind: handoff in its frontmatter — this is the identity stamp /pickup uses to identify handoff files.Canonical specs (do not duplicate here):
${BDC_HOME:-$HOME/.bdc}/standards/ai-friendly-artifacts.md § 1.4.2${BDC_HOME:-$HOME/.bdc}/standards/ai-friendly-artifacts.mdWhy one schema, not two: the previous design had a Schema A (<project>/HANDOFF.md, multi-branch index) alongside Schema B (this one). Schema A's per-branch sections required parsing markdown bodies (## {branch} headers, **Topic**: fields, fallback bullets) — fragile against any project that hand-wrote a HANDOFF.md with free-form ## sub-sections, which leaked into /pickup as ghost rows. The fix is to make identity explicit at write-time (kind: handoff stamp in frontmatter) and let /pickup trust the stamp instead of guessing structure.
This skill is the executor. To change format rules, edit the canonical doc, not this skill.
Every session that touched code/config writes to one file: ${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.md.
${BDC_HOME:-$HOME/.bdc}/handoff/*.md for an existing topic match (frontmatter title or filename slug). If found → update in place. If not → create a new file ${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.md (slug only, no date prefix; date lives in frontmatter).launch-traffic-fix, pickup-display-bugs). Pick something that survives across sessions even if the headline phrasing changes.Determine "did this session touch code/config" by inspecting Edit/Write tool calls in the conversation + git status in relevant dirs. If yes → write a handoff. If no → exit without writing.
After the target file path is known but before Phase 2 writes anything, check whether the SAME target was just written. Repeat invocations within a few minutes overwrite a fresh handoff with potentially-worse extraction (same conversation context, different field inference).
Rule: compute latest_mtime = max mtime across:
${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.mdFiles that do not exist contribute nothing — a fresh topic with no prior handoff trivially passes the guard.
If now - latest_mtime < 300 seconds, abort with a one-line prompt to builder: ⏸ /handoff just ran <Ns> ago against <target_path>. Re-run anyway? (yes / no). Wait for an explicit yes before continuing.
Skip the guard only when builder explicitly says "force" / "重跑" / "再跑一次" in the same message that invoked the skill.
Why scoped to the target file (not the whole directory): scanning ${BDC_HOME:-$HOME/.bdc}/handoff/ as a whole would let an unrelated concurrent session block this one. The scope must be the target topic.
Phase 2 emits the HANDOFF file with the structured session brief (9-field YAML). Generate the brief once per invocation (§ 2.1), then apply the write rules (§ 2.2).
The 9-field brief is an AI-first artifact. Do not replace it with prose or rename fields. Instead, preserve stable fields, exact paths, status, evidence, and next action. Because: the next AI and /pickup need a parseable recovery surface.
The ## Last session brief section is a 9-field YAML block. Per-field sources:
date: today (date +%Y-%m-%d).
plan_progress: if an active plan in .bdc/plans/ is being executed, format "step N/M → N+1/M (advanced K steps)" where N = - [x] count at session start, N+1 = current - [x] count, M = total checkboxes, K = flips this session. No active plan → omit field.
files_touched: 4 sources merged, each entry labeled with origin. The skill explicitly does NOT auto-commit, so working-tree sources are the norm — missing them would silently drop the most active session work.
git log <start>..HEAD --stat → entry as path (+24/-18, committed)git diff --cached --stat → entry as path (+5/-2, staged)git diff --stat → entry as path (+12/-0, unstaged)git status --short filtered to ^?? → entry as path (untracked)<start> comes from § Phase 2.5 (last-handoff-hash). Don't merge entries by path — origin labels carry the meaning, so the same file in committed + staged stays as two rows.
commits: git log <start>..HEAD --pretty=format:'%h %s', one entry per commit.
key_decisions: extract explicit decisions from the session, plan Decision Log, commits, and builder corrections; one bullet per decision, trimmed to one line.
discoveries: extract insights, mistakes, pitfalls, or useful findings from the session; one bullet per discovery, trimmed to one line.
why_stopped: AI-inferred one-liner. Prefix value with inferred: so builder can overwrite on review. Best guess based on the conversation's last few turns (planned pause vs blocker vs end-of-task).
next_step: first - [ ] action line from the active plan's currently-executing feature. No active plan → use the first concrete pending item from the session.
open_questions: only items builder explicitly flagged as questions for the next session. Otherwise empty list [].
Empty list [] is preferred over omitting a field — keeps the schema stable for downstream parsers (the /pickup skill consumes this brief).
kind: handoff — fixed string, the identity stamp /pickup uses to recognize handoff files. Without it the file will be invisible to /pickup. Other handoff frontmatter (status, last_session, prs, etc.) per § 1.4.2.## Last session brief → replace with § 2.1 output (always current; structured).## Next → replace (always current; human-readable paraphrase of brief.next_step if more context helps).## Constraints → edit (add new, remove what no longer applies, keep what's still in force)## Known Pitfalls → append (累积; each gotcha saves the next session from repeating)## Related PRs → update to current state via gh pr viewlast_session → todayprs → reflect current setstatus → active unless all PRs merged + no pending follow-up (then done)The brief's files_touched and commits fields are computed by git log <start>..HEAD. <start> is the HEAD as of the previous /handoff invocation; this section maintains that anchor.
Write step (end of each /handoff):
git rev-parse HEAD > .git/last-handoff-hash in each touched project repo. This file is per-repo, never committed (.git/ is git's own state directory)..git/last-handoff-hash independently.${BDC_HOME:-$HOME/.bdc}/ or other non-git locations, skip this step — there is no diff to compute.Read step (start of each /handoff):
cat .git/last-handoff-hash and git cat-file -e <hash> to confirm reachability.git log <hash>..HEAD is the diff range for files_touched / commits.git log --since='<last_session timestamp>', where <last_session timestamp> reads from the target handoff's frontmatter last_session field. If not parseable → drop to Fallback B.commits: [] is genuinely empty (no diff range = no commits). Do not block the handoff. The brief annotates the committed-source omission inline: files_touched: ... # committed source unavailable: <reason>.Why .git/: it is per-repo, gitignored by definition, and survives across sessions while never leaking into commits. No new top-level files outside the repo's own state.
Update ${BDC_HOME:-$HOME/.bdc}/HANDOFF.md Active list:
## Next- [<topic-slug>](handoff/<topic-slug>.md) — <one-line description matching ## Next>status: done → can be removed from Active list (or moved to a "Recently done" section if builder prefers)Print 4 lines to chat (builder copy-pastes into the next session):
📁 <handoff path>
🚪 Enter next time: cd <absolute path>
🎯 First step next session: <one sentence — extracted from ## Next>
⚠️ Unresolved: <git uncommitted · open PR · ${BDC_HOME:-$HOME/.bdc}/ untracked, etc.>
## Next into one sentence with a concrete action verbcd ${BDC_HOME:-$HOME/.bdc} && git status --short | head -5 — if untracked/modified, mention it in ⚠️If ## Next has no clear concrete action → ask builder one question: "Where should the next session start?" — then finalize the handoff.
kind: handoff from frontmatter — without the stamp, /pickup ignores the file. Treat this as the identity contract between writer and reader; everything else (sort order, badges, brief parsing) downstream depends on the file being recognized as a handoff first.<project>/HANDOFF.md — the legacy Schema A path. Project-local handoffs are obsolete; everything goes to ${BDC_HOME:-$HOME/.bdc}/handoff/<topic-slug>.md.kind: handoff.files_touched ignores unstaged or untracked changes.| Rationalization | Response |
|---|---|
| "A short chat summary is enough." | Do not. Pattern: long-running validation work requires durable next-session state instead of chat-only summaries. |
| "This topic changed, so create a new handoff file." | Do not unless it is a truly new topic. Incident: this skill's own anti-pattern history replaced fragile project-local Schema A with one stable kind: handoff identity stamp. |
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub 0xmariowu/builders-dont-cry