agentleFS
smarzbansmarzban/agent-sdlc/skills/handoff/SKILL.md

handoff

Skill2 starsChanged 4 months agoApache-2.0View on GitHub

Scaffold, update, or prune a repo's HANDOFF.md: the live 'where we left off' doc the next agent (any agent, any day) reads to resume, distinct from the standing rules in the repo's agent-instruction files. Selects one of exactly three modes mechanically: scaffold when no handoff doc exists, update to write the current entry, prune when a mechanical size/structure/content trigger has fired. Triggers: 'set up a handoff doc', 'write a handoff', 'update the handoff', 'prune the handoff', 'where did we leave off', or any time a session is ending mid-work and the next agent needs to resume without asking. Scope: standalone, for any repo, independent of any pipeline.

191 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use decision-mapping, setup-ts-deep-modules and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • FabienDehopreSkill

    decision-mapping

    Turn a loose idea into a sequenced map of investigation tickets, then drive them to resolution one at a time.

    Also in 8 projects that use this

  • liufeng1976Skill

    setup-ts-deep-modules

    Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files. User-invoked.

    Also in 42 projects that use this

  • AWSSkill

    to-questionnaire

    Turn a decision you can't fully answer into a questionnaire for someone else to fill in.

    Also in 93 projects that use this

  • GoogleSkill

    ask-matt

    Ask which skill or flow fits your situation. A router over the skills in this repo.

    Also in 87 projects that use this

  • RoentekSkill

    loop-me

    Grill me about specs for the workflows I want to build, within this workspace.

    Also in 51 projects that use this

  • LesaniSkill

    writing-beats

    Shape an article as a journey of beats, choose-your-own-adventure style. The user picks a starting beat from the raw material, you write only that beat, then offer options for where to pivot next, beat by beat, until the article reaches a natural end. Use when the user has raw material and wants to assemble it as a narrative rather than an argument.

    Also in 54 projects that use this

The file

We split this skill into the rules it gives an agent

12 rules. 0 are common, 9 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 172 edited versions we read keep these.

  • 9

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If none of these clearly holds, say so explicitly and ask which is intended rather than choosing.

    line 62
  2. Very specific:

    Create HANDOFF.md at the repo root from the template below.

    line 66
  3. Very specific:

    Add HANDOFF.md to the repo's ignore file if it is not already ignored, and tell the user you did this, with the tradeoff in one line:

    line 69
  4. Distinctive:

    If the repo has no place for standing rules yet, say so: this skill never creates that file, it only names where standing-rule content from …

    line 75
  5. Distinctive:

    If the doc exists but does not match the template's contract (no header, no current-state section, or sections out of order), materialize …

    line 80
  6. Distinctive:

    Check the prune trigger first (mechanical, below), and check whether an owed-prune marker is already under the header.

    line 84
  7. Very specific:

    Check the ignore-file entry (git check-ignore) unless HANDOFF.md is already tracked by git (the deliberate-commit case, per Conventions): …

    line 103
  8. Distinctive:

    Write the file: the trimmed result from step 1 if the trigger fired and a user confirmed it, otherwise the edited file as-is (with the …

    line 110
  9. Distinctive:

    Only after that confirmation, write the trimmed file (and the eviction target, if anything moved).

    line 132
  10. Distinctive:

    The litmus decides placement, once: Every other rule in this skill is a consequence of it, not a second opinion.

    line 174
  11. Distinctive:

    Never create by surprise: Only scaffold creates the file, per its condition above.

    line 183
  12. Distinctive:

    Never executes anything: this is a prose discipline, not tooling.

    line 294

“Common” is measured: kept by at least 6 in 10 of the 172 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Handoff: keep the working-copy state doc honest

A repo's git history records what changed, never what was being attempted. HANDOFF.md is the doc that carries the working copy forward: what is in flight, what was deliberately left undone, which of two plausible readings of the repo is current. Its known failure mode is becoming an append-only log of merged PRs and shipped features, at which point it costs a read and returns stale context. Everything here exists to make that failure mechanical to catch, not a matter of remembering to be disciplined.

<HARD-GATE> Exactly three modes exist: scaffold, update, prune. Scaffold only when no `HANDOFF.md` exists at the repo root AND creating one was asked for or is clearly needed to resume work: never scaffold on absence alone, unasked. Update is the default for writing today's entry, and it always runs the prune trigger check first: when the trigger has fired, update disposes of every entry per the litmus (evict standing rules, collapse or drop closed threads) and shows the trimmed result before overwriting, in the same pass, rather than deferring the prune or telling the user to run it separately. The one exception is a hook-driven update with no user present to confirm: the first such update after the trigger fires writes the current entry, never prunes inline, and instead records under the header that a prune is owed, which leg fired, and the header stamp at that moment, then announces all three (see Update, hook-driven case, below). That deferral covers exactly one update. Any later update that finds the marker still there must resolve it now: prune inline if a user is present, or, if it is again hook-driven with no user present, skip the write entirely and announce that the handoff was not updated rather than deferring a second time. Prune stays separately invocable, for when the user asks for a prune pass on its own without writing a current entry, or to clear an owed-prune marker a hook left behind. If the mode still cannot be determined this way, say so and ask rather than picking: scaffold and prune differ in whether anything is destroyed, and guessing wrong on that axis is the one mistake this skill cannot recover from. A prune pass, standalone or inline inside an update, never overwrites `HANDOFF.md` before showing the user the trimmed result. Content identified as a standing rule is evicted (moved to the repo's agent-instruction files, per the litmus), never deleted outright; deletion is reserved for threads that are already merged, shipped, closed, or decided. No mode creates or writes anything outside `HANDOFF.md`, its one ignore-file entry, and (on eviction) the repo's agent-instruction files. </HARD-GATE>

The litmus (state once; every placement call below refers back to it)

Will this be wrong next week, or still true next month?

  • Wrong next week (an in-flight branch, today's repo state, a half-formed suspicion, a "next up") -> it is live state: it belongs in HANDOFF.md.
  • True next month (a convention, a tool gotcha, "how we work here", a durable lesson) -> it is a standing rule: it belongs in the repo's agent-instruction files, not here.

Nowhere else in this skill restates that test; every mode, the prune trigger, and eviction all just point back to it.

The three modes

Pick exactly one, by this condition, before doing anything else:

ModeCondition
ScaffoldNo HANDOFF.md exists at the repo root, AND creating one was asked for or is clearly needed to resume work. Never on absence alone, by surprise.
UpdateHANDOFF.md exists and the current entry needs writing (a session is starting, in progress, or ending). Always runs the prune trigger check first; disposes of entries inline and shows the trimmed result if the trigger has fired, before writing.
PruneThe user asks for a prune pass on its own, separate from writing today's entry.

If none of these clearly holds, say so explicitly and ask which is intended rather than choosing.

Scaffold

  1. Create HANDOFF.md at the repo root from the template below.
  2. Fill the stamped header line and a short current-state entry from what is actually true right now (branch, head, clean/dirty, what is in flight): never a placeholder.
  3. Add HANDOFF.md to the repo's ignore file if it is not already ignored, and tell the user you did this, with the tradeoff in one line:

    Ignored by default: private and safe for half-formed notes and local paths, but it never travels with the repo unless the team opts in to commit it instead.

  4. If the repo has no place for standing rules yet, say so: this skill never creates that file, it only names where standing-rule content from a future prune would go (per the litmus).

Update

If the doc exists but does not match the template's contract (no header, no current-state section, or sections out of order), materialize it to the template first: show the rewritten result before overwriting, the same as any lossy rewrite, then continue with the steps below.

  1. Check the prune trigger first (mechanical, below), and check whether an owed-prune marker is already under the header. Handle in this order:

    • A user is present and the trigger has fired (marker present or not): dispose of every entry per the litmus (evict standing rules, collapse or drop closed threads), show the trimmed result to the user before continuing, exactly as prune's show-before-overwrite step does, and clear any owed-prune marker in the same pass.
    • No user is present and an owed-prune marker is already there: the one update of deferral it bought already ran. Skip this update entirely, write nothing, and announce: prune owed since the stamped update, handoff not updated. A hook cannot show a show-before-overwrite result, so it does not get a second deferral.
    • No user is present, the trigger has fired, and no marker exists yet (hook-driven case): never prune inline. Write the current entry (steps 2-5 below) as usual, add one line under the header naming the leg that fired and carrying the exact header stamp at the moment it is written (the same mechanic as an overrun note, below), and say so in the announcement. Leave the disposal for a user-run prune pass, or, if none comes, the next update resolves it per the case above.
  2. Rewrite the current-state section fresh: do not append below the old entry. State what just happened and the true current repo state (branch, head, clean/dirty, what shipped).
  3. Check the ignore-file entry (git check-ignore) unless HANDOFF.md is already tracked by git (the deliberate-commit case, per Conventions): if it is untracked and not ignored, add the entry and say so, the same as scaffold step 3.
  4. Rewrite next-up to the true next action.
  5. Move any thread that closed this session out of open threads: collapse it to a one-line pointer, or drop it if nothing about it is worth keeping.
  6. Refresh the stamped header line.
  7. Write the file: the trimmed result from step 1 if the trigger fired and a user confirmed it, otherwise the edited file as-is (with the owed-prune line, in the hook-driven case). Either way, this is still one mode: update. (The already-deferred hook case in step 1 exits before this step: nothing is written.)

Prune

Condition to enter this mode on its own: the user asks for a prune pass, separate from writing today's entry. (An update that hits a fired trigger runs the same disposition inline; see Update step 1, above. An update that instead left an owed-prune line under the header, per Update step 1's hook-driven case, is the other on-ramp: running prune here clears that line.)

  1. Dispose of each entry per the litmus: eviction rules below cover standing rules; a thread that is already merged, shipped, closed, or decided is collapsed to a one-line pointer or dropped.
  2. Evict before you delete. Default disposition for anything that is a standing rule: it MOVES to the repo's agent-instruction files, because HANDOFF.md is often the only copy of that context and deleting it destroys information the litmus says belongs elsewhere, not information that has expired. Deletion is reserved for threads that are already merged, shipped, closed, or decided, with nothing worth keeping.
  3. Show the trimmed result to the user before overwriting the file, and say why: pruning is lossy, so the user confirms what is relocated versus cut rather than discovering it after the fact.
  4. Only after that confirmation, write the trimmed file (and the eviction target, if anything moved).

The prune trigger (mechanical, evaluable at a glance)

Fires the moment ANY of the following is true. It is not a judgement call:

  • Size: the doc is more than 100 lines long (about a screen: the reason for the number, not the rule itself). This is the weakest leg: the incident that motivated this skill ran 83 lines, comfortably under 100, and a size check alone would not have caught it. A freshly pruned doc for a busy repo is roughly that length too, so a lower ceiling would fire on a healthy doc as often as a stale one. Treat 100 as a backstop, not the primary catcher: that role belongs to the content leg below, which is what actually catches an append-only log (the motivating doc was pages of "merged", "shipped", "closed" entries, exactly what the content leg flags).
  • Structure: the current-state section is not the first thing after the header line, meaning the stamped _<date> · by: ... line (the fifth line of the template), not the # HANDOFF title. The current-state section is whatever section comes first after that line, whatever it is titled.
  • Content: any entry describes something already merged, shipped, closed, or decided, regardless of the words used to describe it. The current-state section step 2 of Update mandates writing is exempt: it always states the true current state, including what just shipped, and that is never staleness. The leg applies to the other sections (next up, open threads, gotchas).

A deliberate overrun is allowed only when the doc states, under the header, which leg it is excusing (size, structure, or content), why, and the exact stamp value from the header line at the moment the note was written. The note is honored only while its stamp still matches the current header stamp: the update that wrote it. Any update after that one, once it refreshes the stamp per Update step 6, prunes regardless of what the note says. Never rewrite the same leg's note with a fresh stamp to keep it alive: a leg that needs excusing twice is the signal to prune it, not to renew the waiver.

An owed-prune marker (the hook-driven case's deferral line, see Update above) follows the same mechanic. It carries the exact header stamp at the moment it is written, and it excuses deferring the prune for exactly one update: the one that wrote it. Any later update that finds the marker already there, its stamp necessarily from before that update's own, must resolve it now rather than restating it: prune inline and show the result if a user is present, or, if it is again hook-driven with no user present, skip the write entirely and announce that the handoff was not updated. Restating the marker under a fresh stamp instead of resolving it is the same forbidden move as renewing an overrun note.

Principles

  • The litmus decides placement, once. Every other rule in this skill is a consequence of it, not a second opinion.
  • The trigger is mechanical, not a vibe. "It still feels useful" is not one of the three conditions; if size, structure, or content has tripped, prune runs regardless of how useful the content feels.
  • Eviction is the default, deletion is the exception. Losing a standing rule because it lived in the one doc nobody re-reads is a worse failure than a slightly-early prune.
  • Show before you cut. A lossy operation asks first; it does not apologize after, whether the prune runs standalone or inline inside an update.
  • Never create by surprise. Only scaffold creates the file, per its condition above; update and prune both require it to already exist.
  • Ignored is the default, not the only option. State the tradeoff once and let the team override it; do not silently assume everyone wants the same answer.

Rationalizations (excuses to skip the bar, and the rebuttal)

ExcuseRebuttal
"I'll prune it next time."The trigger already fired. "Next time" is how every append-only log got that way.
"This merged PR is still useful context."If it is a standing rule, evict it to the agent-instruction files where it survives. If it is not, the PR and the git log already have the full story; a pointer line is enough.
"The user can just read the git log."Git records what changed, never what was being attempted or deliberately left undone. That gap is the entire reason this doc exists.
"It's only a few extra lines over the trigger."The trigger fires at the line: more than 100 lines, no fuzz. A stated, deliberate overrun is the only sanctioned exception, and it is honored only for the update whose stamp it carries: the next update prunes regardless.
"I'll just restate the overrun note so it stays valid."Rewriting the same leg's note with a fresh stamp is forbidden. A leg that needs excusing twice is the signal to prune it, not to renew the waiver.
"I'll just leave the owed-prune line, the hook will get it next update."The deferral is good for one update only. The next update either prunes inline (user present) or skips the write and says so (hook, no user present); restating the line is the same forbidden renewal as an overrun note.
"I didn't have time to update it."An update pass is short by design: rewrite current-state, next-up, and closed threads. Skipping it is what hands the next agent nothing.
"I'll just delete the stale-looking stuff, it's faster."Delete is for threads that are already merged, shipped, closed, or decided. Anything that reads like a standing rule gets evicted, not deleted, because this may be its only copy.
"The doc already exists, I'll scaffold a fresh one to clean it up."That is a prune, not a scaffold, and it skips the show-before-overwrite step scaffold doesn't have. Pick the mode the condition actually selects.

Red flags (stop and fix)

  • An entry that is a standing rule, sitting in HANDOFF.md past the next prune pass instead of being evicted.
  • A prune or an overwrite of HANDOFF.md with no trimmed-result confirmation shown first, whether the prune ran standalone or inline inside an update.
  • The current-state section is not the first thing after the header, and no prune has run.
  • An entry that is already merged, shipped, closed, or decided, outside the current-state block, left in place with no stated overrun note, or with a note whose stamp does not match the current header stamp.
  • An overrun note for the same leg rewritten with a fresh stamp instead of pruning that leg.
  • HANDOFF.md created or updated without adding (or checking) the ignore-file entry.
  • The mode was guessed rather than determined by the condition table, especially scaffold-vs-prune.
  • An update that left the prune trigger fired without disposing of entries inline, and without a user present to justify deferring it via the owed-prune line.
  • An owed-prune line still present at a later update (its stamp no longer that update's own) that was neither pruned inline nor caused a hook to skip the write and announce as much.
  • Placement reasoning restated inline instead of pointing back to the litmus.

Done when

  • Exactly one mode ran, selected by its stated condition (or the ambiguity was surfaced and asked about instead of guessed). An update that pruned inline because the trigger fired still counts as one mode: update.
  • On scaffold: the file exists from the template, the current-state entry is real (not a placeholder), the ignore-file entry is added or confirmed, and the user was told both, including the one-line tradeoff.
  • On update: the prune trigger and any existing owed-prune marker were checked first. If the trigger fired and a user was present, entries were disposed of per the litmus, any owed-prune marker was cleared in the same pass, and the trimmed result was shown before the write. If the trigger fired with no user present and no marker existed yet, the current entry was written, the disposal was deferred, and an owed-prune marker naming the fired leg and carrying the header stamp was added and announced instead. If no user was present and a marker already existed, the update was skipped entirely and "handoff not updated" was announced instead of writing. Whenever the file was written: current-state and next-up are rewritten fresh, closed threads are collapsed or dropped, the ignore-file entry was checked, and the stamp is refreshed.
  • On prune (standalone): every entry was checked against the litmus, standing rules were evicted rather than deleted, the trimmed result was shown and confirmed before the overwrite, any owed prune-line was cleared, and any deliberate overrun left in place carries the current header stamp.

The artifact (output)

HANDOFF.md at the repo root, from this template (the sections are the doc's contract; keep them in this order and this short):

# HANDOFF: live working state

Where we left off, for the next agent (any agent, any day). Standing rules live in the repo's
agent-instruction files, not here (see the litmus). Keep this short and current: prune on the
trigger, don't append forever.

_<date> · by: <agent> · <branch> @ `<short-sha>` (<clean/dirty>)_

## Current state
<One short paragraph: what just happened, what state the repo is in, anything in flight now.>

## Next up
- <What the next agent should pick up first.>

## Open threads
- <In-flight branches / PRs / undecided questions, one line each plus a pointer. Collapsed or
  dropped once closed, per the litmus and the prune trigger.>

## Gotchas
- <Narrative git history alone doesn't capture. Pruned aggressively, evicted when durable.>

On scaffold, also: an ignore-file entry for HANDOFF.md, and a stated line to the user that it was added plus the one-line ignored-versus-committed tradeoff.

A deliberate overrun note or an owed-prune line, when either applies, is one line placed immediately after the stamp line and before ## Current state. Both quote the header's own stamp verbatim at the moment they are written, so either can be checked against the current header stamp later: an overrun note as _Overrun (size): keeping the full incident writeup one more update, stamp 2024-03-01 · by: fixer · main @ a1b2c3d (clean)._, an owed-prune line as _Prune owed: content leg fired, stamp 2024-03-01 · by: fixer · main @ a1b2c3d (clean), deferred by the pr-review hook._. Neither is renewed under a fresh stamp: a note or marker still present at the next update gets resolved (pruned inline, or the write skipped), not restated.

On prune (standalone or inline inside an update), also: the trimmed result shown for confirmation before it overwrites the file, and, where anything was evicted, the addition to the repo's agent-instruction files carrying that content.

Conventions

  • One file, one repo root, one contract: HANDOFF.md, matching the template above. No second copy, no per-branch variant.
  • Ignored by default per working copy; a team that wants it shared commits it deliberately and drops the ignore-file entry, having read the one-line tradeoff first.
  • Never executes anything: this is a prose discipline, not tooling. No backup, sync, or restore step is part of it; if a repo wants durability beyond the working copy, that is a separate, explicit decision outside this skill's scope.
  • Vendor-neutral throughout: readable and writable by any agent or any human, never assuming a specific tool's config layout or a specific person's remote.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.