Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .agents/skills/handoff/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ When the human types `/handoff` (or "next session", "what's next", "handoff note

Handoff may not emit while any background task spawned this conversation is unresolved: await finite work, act on landed-but-unhandled results, retire session daemons, or (narrowly, never for a verification task) abandon with the reason written down. Right after wrap-up this is a quick re-check that nothing new was spawned; standalone, the full barrier applies first. See AGENTS.md § The background-work barrier.

## Pull request authority

Invoking handoff is explicit permission to resolve pull requests created by the work being handed off. Do this before writing the handoff note:

1. Inspect the exact head commit, base branch, required reviews, and checks.
2. If the pull request is ready and the work is meant to ship, merge it using the repository's merge policy. Never bypass a failed check or required review.
3. If the pull request is superseded, abandoned, or should not ship, close it without merging.
4. Prove the final GitHub state, clean up the resolved branch, synchronize the base branch, and verify any resulting base-branch check.

This authority is narrow. It covers pull requests created by this session or explicitly named in the work being handed off. It does not cover unrelated pull requests, product or provider changes, spending, deployment, or any other external effect. If an in-scope pull request cannot be resolved safely, leave it open and state the exact blocker in the handoff.

## Voice

The output IS chat to the human: follow AGENTS.md § Voice, including in every fill-in. Pipe the draft through `node scripts/voice-gate.js` before sending.
Expand Down Expand Up @@ -74,10 +85,10 @@ If nothing's left over and nothing qualifies, output exactly: `Nothing to hand o

The recommended focus picks ONE thing and justifies it in a sentence. Don't offer a menu.

## Read-only
## Repository boundary

Handoff modifies no files. It produces the block and stops.
After resolving in-scope pull requests, handoff modifies no product or documentation files. It produces the block and stops. The Git and GitHub state changes described above are the only writes this command authorizes.

## Rationale (recorded so future edits don't drift it)

The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The pull request step belongs before the note because handing off a ready or obsolete pull request just makes the next session repeat the end of this one. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
17 changes: 14 additions & 3 deletions .claude/commands/handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ When the human types `/handoff` (or "next session", "what's next", "handoff note

Handoff may not emit while any background task spawned this conversation is unresolved: await finite work, act on landed-but-unhandled results, retire session daemons, or (narrowly, never for a verification task) abandon with the reason written down. Right after wrap-up this is a quick re-check that nothing new was spawned; standalone, the full barrier applies first. See AGENTS.md § The background-work barrier.

## Pull request authority

Invoking handoff is explicit permission to resolve pull requests created by the work being handed off. Do this before writing the handoff note:

1. Inspect the exact head commit, base branch, required reviews, and checks.
2. If the pull request is ready and the work is meant to ship, merge it using the repository's merge policy. Never bypass a failed check or required review.
3. If the pull request is superseded, abandoned, or should not ship, close it without merging.
4. Prove the final GitHub state, clean up the resolved branch, synchronize the base branch, and verify any resulting base-branch check.

This authority is narrow. It covers pull requests created by this session or explicitly named in the work being handed off. It does not cover unrelated pull requests, product or provider changes, spending, deployment, or any other external effect. If an in-scope pull request cannot be resolved safely, leave it open and state the exact blocker in the handoff.

## Voice

The output IS chat to the human: follow AGENTS.md § Voice, including in every fill-in. Pipe the draft through `node scripts/voice-gate.js` before sending.
Expand Down Expand Up @@ -67,10 +78,10 @@ If nothing's left over and nothing qualifies, output exactly: `Nothing to hand o

The recommended focus picks ONE thing and justifies it in a sentence. Don't offer a menu.

## Read-only
## Repository boundary

Handoff modifies no files. It produces the block and stops.
After resolving in-scope pull requests, handoff modifies no product or documentation files. It produces the block and stops. The Git and GitHub state changes described above are the only writes this command authorizes.

## Rationale (recorded so future edits don't drift it)

The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The pull request step belongs before the note because handing off a ready or obsolete pull request just makes the next session repeat the end of this one. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
17 changes: 14 additions & 3 deletions .cursor/commands/handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ When the human types `/handoff` (or "next session", "what's next", "handoff note

Handoff may not emit while any background task spawned this conversation is unresolved: await finite work, act on landed-but-unhandled results, retire session daemons, or (narrowly, never for a verification task) abandon with the reason written down. Right after wrap-up this is a quick re-check that nothing new was spawned; standalone, the full barrier applies first. See AGENTS.md § The background-work barrier.

## Pull request authority

Invoking handoff is explicit permission to resolve pull requests created by the work being handed off. Do this before writing the handoff note:

1. Inspect the exact head commit, base branch, required reviews, and checks.
2. If the pull request is ready and the work is meant to ship, merge it using the repository's merge policy. Never bypass a failed check or required review.
3. If the pull request is superseded, abandoned, or should not ship, close it without merging.
4. Prove the final GitHub state, clean up the resolved branch, synchronize the base branch, and verify any resulting base-branch check.

This authority is narrow. It covers pull requests created by this session or explicitly named in the work being handed off. It does not cover unrelated pull requests, product or provider changes, spending, deployment, or any other external effect. If an in-scope pull request cannot be resolved safely, leave it open and state the exact blocker in the handoff.

## Voice

The output IS chat to the human: follow AGENTS.md § Voice, including in every fill-in. Pipe the draft through `node scripts/voice-gate.js` before sending.
Expand Down Expand Up @@ -67,10 +78,10 @@ If nothing's left over and nothing qualifies, output exactly: `Nothing to hand o

The recommended focus picks ONE thing and justifies it in a sentence. Don't offer a menu.

## Read-only
## Repository boundary

Handoff modifies no files. It produces the block and stops.
After resolving in-scope pull requests, handoff modifies no product or documentation files. It produces the block and stops. The Git and GitHub state changes described above are the only writes this command authorizes.

## Rationale (recorded so future edits don't drift it)

The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
The two-half shape exists because the note serves two readers with opposite needs: the human skims the top in ten seconds to remember where things stand, and the next session's agent executes the bottom verbatim. Mixing the two produces a note neither reader trusts. The pull request step belongs before the note because handing off a ready or obsolete pull request just makes the next session repeat the end of this one. The explicit STOP instruction in the agent half is load-bearing: without it, next session's agent tends to run the whole chain uninvited, and the human loses the checkpoints the process exists for.
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Once build-it starts, the chain runs without questions to the human until handof

No session closes while anything it spawned is still running or unread: a subagent, a background shell, a pending check. Await it, act on it, retire it, or explicitly abandon it with the reason written down. A verification task can never be abandoned. Closing a session around pending work ships an incomplete session.

Invoking handoff is explicit, narrow permission to resolve pull requests created by the work being handed off. Merge a ready pull request when the work is meant to ship, or close it without merging when it is superseded or abandoned. Prove the final state, clean up its branch, synchronize the base branch, and verify the resulting check. This permission never reaches unrelated pull requests, bypassed reviews, failed checks, deployments, spending, or provider changes.

## Voice

Everything written for a human reads like a person wrote it: plain words, short sentences, no jargon stacks, no em dashes. Final plan prose, frame-it, quiz, wrap-up, and handoff run through `node scripts/voice-gate.js`. Other responses use the live gate when the agent itself suspects the draft is dense or awkward; short structured stage reports rely on their fixed shape. The same list also has teeth: `npm run check` fails when a listed phrase lands in committed prose (docs, command files, code comments), so the voice bar holds without anyone remembering to hold it. The gate only knows the phrases in `scripts/phrase-list.json`; wrap-up's jargon step grows that list every session, so it keeps pace with the jargon you actually produce. For vocabulary the list doesn't know yet, the test is: would someone outside the codebase follow this sentence? If not, rewrite it.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ What you actually type. Each stage is a slash command (the filename becomes the
2. **construct-the-plan**, and read the narrative half it writes for you.
3. **frame-it**, and answer its three to five questions. This is your last required moment at the keyboard.
4. Queue **challenge-plan** rounds one through five, **build-it**, **test-it**, **challenge-implementation** rounds one through five, and **wrap-up**. Walk away; every stage after frame-it runs unattended.
5. Come back to one pull request and a plain-English summary of what you now have. **handoff** when you want a bridge to next time, **quiz** when you want to be tested on what shipped.
5. Come back to one pull request and a plain-English summary of what you now have. **handoff** when you want a bridge to next time. Invoking it also authorizes the agent to merge a ready pull request from that work, or close it without merging when it should not ship. Use **quiz** when you want to be tested on what shipped.

## Getting started

Expand Down
27 changes: 27 additions & 0 deletions tests/handoff-pr-authority.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..');

function read(relativePath) {
return readFileSync(join(repoRoot, relativePath), 'utf8');
}

test('handoff explicitly authorizes resolving its own pull requests', () => {
const command = read('.cursor/commands/handoff.md');
const agreement = read('AGENTS.md');
const readme = read('README.md');

assert.match(command, /Invoking handoff is explicit permission to resolve pull requests created by the work being handed off/);
assert.match(command, /merge it using the repository's merge policy/);
assert.match(command, /close it without merging/);
assert.match(command, /Never bypass a failed check or required review/);
assert.match(command, /It does not cover unrelated pull requests/);
assert.doesNotMatch(command, /## Read-only/);

assert.match(agreement, /Invoking handoff is explicit, narrow permission to resolve pull requests/);
assert.match(readme, /authorizes the agent to merge a ready pull request/);
});