用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/fade/dsmr-mcp --skill rotate-context-window命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Rebuild a leader's repo-local context AND the whole-fleet context in one pass, so the leader can assert positive, rapid control over every worker on ITS OWN bus. Use when you are a leader starting or resuming a session, after a fleet restart, when you have lost track of fleet state, or whenever the operator types /leader. More than one leader may be running, each with its own bus and its own fleet. Reads files, git, and the bus roster verbs, never reconstructing state from bus conversation.
Rebuild a worker (sister) agent's repo-local context, announce to the leader in one line, revalidate the leader/worker contract, PRE-CLEAR every permission the assigned work needs so a denial never blocks mid-task, and arm a watch on each joined bus while proving it is the right one. Takes an optional fleet tag. Use when you are a sister agent starting or resuming a session, after a fleet restart, or whenever the operator types /worker.
Read a PR body, commit message, diff comments, or any artifact about to be placed in front of the operator, as a person who knows the codebase and did not attend the session that produced it. Asks first whether the text reads as its author writing about his own work, since an impersonal body is a defect and not a clean result, then catches register and voice failures no pattern match can express: personification, an agent's first person, relay attribution, bus vocabulary, prose narrating how instead of what and why, and summaries answering the adjacent question. Use before any PR is filed, before any review buffer or handoff file is displayed, and whenever the operator asks for a copy-edit pass.
正在显示 SKILL.md
| name | rotate-context-window |
| description | Snapshot in-flight state to a handoff file so the next instance picks up cleanly without re-prompting. |
| allowed-tools | ["Read","Write","Bash","Edit"] |
This skill is silent. Gather state, write the file, print one short incantation the operator can paste into the fresh instance.
In priority order:
<repo-root>/.planning/ exists (GSD project), write to
<repo-root>/.planning/CURRENT-CONTEXT.md. Most GSD setups symlink
.planning/ out of the repo, so the file is automatically untracked.<repo-root>/CONTEXT-HANDOFF.md
and ensure CONTEXT-HANDOFF.md is in .git/info/exclude.~/.claude/handoffs/<basename-of-cwd>.md (mkdir -p).Determine repo root via git rev-parse --show-toplevel. If not a repo,
use pwd.
Run these probes in parallel (one Bash call per cheap group is fine):
Git / branch state
git status --short (filter out permission-denied lines)git branch --show-currentgit log --oneline -10git log --oneline origin/main..HEAD 2>/dev/null if a remote existsgit stash list 2>/dev/null (active stashes)Project state
ls .planning/ if presenthead -80 .planning/STATE.md then tail -30 .planning/STATE.md if presentls .planning/todos/pending/ 2>/dev/null in FULL, and its count.
⛔ Never head this listing. It is the mechanism for not losing notes,
so a silent truncation defeats the only thing it is for.ls .planning/phases/ 2>/dev/null | tail -5 to identify the active phase<active-phase>/README.md exists, head -40 of itPending operator review
~/tmp/<repo-name>-commits/ for staged commit messages and PR bodies
not yet acted on. Match the pattern of "files modified in the last hour"
rather than every file.Cross-repo coordinator state
news-from-sister.md, sister-handoff.md, coordinator-notes.md,
coordinator.md — if any are at repo root or under
.planning/phases/<phase>/, note their existence and the last-modified
time. Read the tail of the most recent one only.Untracked scratch files at repo root
git status --short | awk '/^\?\? / && !/\//{print $2}'Live-image state (Common Lisp projects)
.mcp.json exists and references slynk-attach, note the port.mcp__cl-mcp__repl-eval with a trivial probe like
(list :impl (lisp-implementation-version)). If it returns, the live
image is up; record any system the user mentioned having loaded. If
it errors (slynk drop, attach unavailable, package missing), record
the failure mode — the new instance needs to know whether to cold-boot
or attach.Background processes the harness is tracking
run_in_background is owned by the dying
instance and will not survive. Note them by name and what they were
waiting on so the new instance can re-spawn if needed.Durable IPC bus (dsmr-mcp) — if cross-repo coordination is live
Detect: a bus-status / bus-receive / bus-publish MCP tool is
available, or a dsmr-bus-watch process is among the background
processes, or .planning/ shows active bus coordination. If none of
these, skip — there is no bus to rejoin.
bus-status and record: the agent's stable bus identity, its
namespace, and the pending count. The cursor is server-side and
persists across a client restart — messages posted during the
downtime are NOT lost; the returning instance drains them.dsmr-bus-watch wakeup watch as the one background process
that MUST be re-armed on the other side (it dies with this instance).
Record the exact arm flags in use (e.g. --poll-ms 250 --recycle-seconds 600) so cadence carries over.bus-receive until empty) so the handoff is
written from a known-clean bus state, and note the active coordination
thread (who leads, who is mid-task, what gates the next step).Use this section order. Keep sections terse — the new instance reads this in one pass, not as reference documentation.
# Context Rotation — <ISO timestamp UTC>
## Snapshot
- **Project:** <repo basename> at <absolute repo root>
- **Branch:** <current branch> (<N commits ahead of origin/main, M behind>)
- **Reason:** <operator's stated reason, or "context-window pressure">
## FIRST MOVE — rejoin the durable bus (only if a bus is in use)
Include this section only when a durable IPC bus was detected; drop it
entirely otherwise. When present, it goes FIRST — the next instance
rejoins before anything else, because its identity is reachable the
moment it does and siblings may be waiting.
- Identity `<agent>` is a **stable named bus identity**; its cursor is
server-side and **persisted across the restart** — nothing posted
during the gap was lost.
- Sequence: `bus-receive` (drain everything that landed during the
downtime — read it) → then **re-arm the wakeup watch LAST**:
`dsmr-bus-watch <arm flags from probe>` (run_in_background; **omit
`--after`**; do a catch-up `bus-receive` immediately before arming;
arm strictly after any publish).
Steady-state loop thereafter: drain → act → publish → catch-up drain →
re-arm. A bare heartbeat wake = (no
operator-facing output); only surface on a real message.
restart the Claude for context but keep
the bus MCP server (dsmr-mcp) UP to preserve the live bus + cursors.
The bus WAL is durable, so even an MCP restart recovers — but only
restart it if it is actually wedged; a clean context rotation is not a
wedge.
What the operator most recently asked for, in their own words if
possible. What state that work is in right now. If a slash command or
workflow is mid-execution, name it and the step within it.
Single-line entries, newest first. Decision + outcome, not narration.
Staged commit messages / PR bodies in the
operator has not yet greenlit. Path + one-line subject for each.
up | down | unknown
| none
attached | hermetic | unavailable
yes | no | only-if-X
Sister-handoff / news-from-sister / coordinator files present. Sibling
agent status (running / done / unknown). Most recent log entry's gist.
Skip the section entirely if none present.
file:line — one-line shape
filename — one-line purpose
list or "none".
⛔ Anything flagged in
conversation that has not reached gets written
there as a file BEFORE this handoff is finished. Then list the filenames
below, one line each: .
⚠ A note left here as
prose survives only if the next rotation re-types it, and the one after
that, and every one after that. It reads as recorded for the whole time it
is decaying, which is why nobody notices until it is gone.
⇒ If not, it is not recorded, however carefully it is
written here.
What I tried that didn't work, so the new instance doesn't repeat the
loop. Be specific: command, error class, why it failed, what worked
instead.
Exact paths in the order to read them. Limit to 5; the new instance
should be oriented after this list, not after fishing.
(head 80 + tail 30)
in full. STANDING ENTRY, not a slot to spend:
these are the notes this session was asked to keep, and a note nobody
reads was never kept.
if applicable
Concrete. "Run /gsd-resume-work" or "Continue the active thread by
doing X". One line.
If a section has no content, drop it. Brevity matters more than completeness — the file should be readable in under two minutes.
Skip only if there is no .planning/STATE.md.
⚠ Rotation is the single most likely moment to corrupt GSD state, and the
reason is structural: you are updating STATE.md at the exact moment your
context is most exhausted, when adding is easier than collapsing. The
observed failure is not carelessness, it is a rotating instance appending a
correct new banner above a stale one and considering the job done. Do the six
checks below before writing the handoff; the last one is mechanical and runs the
others' blind spots.
1. ONE banner, and it must be internally CONSISTENT. If STATE.md opens with
a stack of mutually-superseding warnings, a reader has to adjudicate between them
before doing any work. That stack is not a record of diligence; it is the defect.
Collapse it into one banner carrying the current position, the single next action,
and only those facts that are true and easy to misread.
⚠ A count of one is necessary and NOT sufficient — this is the trap's second
form. A single banner piles up contradictions just as readily, and it is harder
to see because nothing looks structurally wrong. One observed banner asserted a
phase complete, repeated that fact in different words, then flatly denied it forty
lines lower, while also carrying a stale main sha and a superseded phase count.
Every one of those was added by an instance that correctly refused to stack a
second banner. ⇒ Read the banner end to end and reconcile it against itself,
then delete the superseded sentences rather than appending a corrective one. If it
no longer fits in one screen, that is the signal it has stopped being a banner.
2. The body lies too. Fixing the banner while leaving a Current Position
section naming a finished phase just relocates the contradiction. Grep for the
prior position's claims (awaiting, pending, next is, the old phase number)
and fix every one, or you have moved the problem rather than solved it.
3. The frontmatter must parse TRUE — and much of it is GENERATED FROM THE BODY,
so fixing the frontmatter alone is silently undone. ⚠ If the project uses
gsd-sdk, its phase operations STRIP the frontmatter and REBUILD it from the
body rather than merging into it. Fields outside its schema are dropped, not
preserved-and-ignored.
⛔ "PARSES TRUE" IS NECESSARY AND NOT SUFFICIENT, and the exception is a
hand-edit hazard that rotation walks straight into. An unquoted YAML scalar
containing a space followed by # terminates there and parses TRUE, silently
discarding the rest of the line. Observed during a fleet park, in a freshly
written last_activity:
last_activity: 2026-07-26, merged PR #40 (fid revocation)
stores '2026-07-26, merged PR'. Run the control before trusting any sweep — one
line, and it is the only thing that shows the class exists at all:
python3 -c "import yaml; print(repr(yaml.safe_load('x: a PR #40 (thing)')['x']))"
# -> 'a PR'
⚠ This sits INSIDE the passing set. It is not the colon-space or
nested-quote class, which THROW and which a safe_load sweep therefore catches.
A repo that passed such a sweep is not thereby clear of this, and the value
reads correctly on the page — nothing looks wrong at any point, which is why it
survives review.
⇒ The check that discriminates is comparing each parsed value against its raw
source text, not merely that the document loads. Quote any scalar containing
# or :.
⚠ Why this lands on rotation specifically: the SDK's own writer quotes
strings containing # or :, so tool-written frontmatter is safe. The hazard
is hand-edited frontmatter — exactly what a rotating instance produces, at
exactly the moment it is least able to notice. Ticket numbers, PR numbers and
issue references are the usual carriers, and they are natural things to reach for
when writing what just happened.
⛔ STATE.md contains MACHINE-INTERFACE LINES that look exactly like prose, and
they sit hundreds of lines apart. Established by observation, not inference:
| Body line | Section it hides in | Regenerates |
|---|---|---|
Last activity: | ## Current Position | frontmatter last_activity |
Stopped at: | ## Session Continuity | frontmatter stopped_at |
Phase: Plan: Status: | ## Current Position | position fields |
The writer sources these from the body with no frontmatter fallback, while the reader prefers frontmatter. The SDK therefore reads values it will not preserve. Two consequences, both observed:
Stopped at: context exhaustion at 75% sat
in ## Session Continuity and repopulated the frontmatter for three days
after the condition passed, surviving direct frontmatter edits. ⇒ Fix the BODY
line. A stale stopped_at makes a cleanly parked repo read as one that died
mid-task.progress: is the genuine exception: the SDK owns it and recomputes it from disk.
Correct it if it is wrong at rest, expect it back, and state the true figure in the
banner along with where it comes from — computed from ROADMAP checkboxes, never
asserted.
⚠ Verify every edit landed. Use a method that reports a substitution count and check it is non-zero. A field that the SDK deleted looks identical to a field you successfully edited if you never check.
4. Archive, never delete. .planning/ is outside git: no diff, no revert,
and rulings recorded nowhere else routinely live in exactly the text you are
about to replace. Move the superseded head to a dated sibling
(STATE.md.superseded-head-<date>-<topic>) and do not clobber an existing
archive — check the name is free first.
5. LENGTH IS ITSELF A DEFECT. Retire before you hand over. A long STATE.md
produces amnesia in the next instance by a concrete mechanism, not vaguely: the
file stops being read whole, so the live position ends up wrapped in history that
is indistinguishable from it, and the machine-interface lines of check 3 end up
hundreds of lines apart where no one meets both.
Observed: 876 lines, of which 54% sat in two sections whose own names admit they
only grow (## Accumulated Decisions, ## Accumulated Context), the live
position was ~20 useful lines inside 173, the two machine-interface blocks were
737 lines apart, and there were two different sections both titled
## Deferred Items. It retired to 212 lines with nothing lost.
The rule: retire at milestone close, and check size at every rotation and every fleet park. Over ~200 lines, retire the oldest closed-phase material first.
Keep only what must be read in order to act: frontmatter, the one banner,
## Current Position, live deferred items, ## Session Continuity, next steps.
Move the rest to .planning/state-archive/<section>-<milestone>.md.
6. Run the triage, and run it LAST. The five checks above are judgement; this one is mechanical and catches the failures that survive judgement:
python3 ~/.claude/skills/rotate-context-window/triage-state.py .planning/STATE.md
It runs its own positive control first and refuses to report at all if the control does not fire, so a clean result cannot come from a check that was never capable of failing. Then: the frontmatter parses; no scalar was silently truncated (check 3); every machine-interface body line is present; banner count and file length are reported as advisories. Exit non-zero means something needs a look.
⚠ A pass is not permission to skip checks 1, 2 and 5. It cannot see a banner that contradicts itself, a body that names a finished phase, or content that should have been retired. It sees only what a regex can decide.
Finally, cross-link STATE.md and the handoff file in both directions. They
are separate entry points and a fresh instance may arrive through either; neither
should be able to orient a reader while leaving them ignorant of the other.
.git/info/exclude, append it.Handoff written: <path>
Paste into the new instance: read <path> and continue.
No other output. The operator is about to restart; long replies are context they will not carry forward.
- Imperative voice in the handoff file. "Branch is at X. Operator approved Y. Next action is Z." - No AI-orchestration vocabulary (no "Phase N Wave M", no "subagent spawned", no "based on memory X"). The handoff is for a fresh instance that will re-discover memory on its own. - No reference to internal planning indices, requirement IDs, or decision tags. The new instance reads STATE.md / phase README and picks those up from there. - The handoff file is operator-grade prose — readable by a human who glances at it before pasting the incantation. - Footer at the bottom of the handoff file matches the project's attribution convention if one applies (check `~/.claude/CLAUDE.md` and the project's `CLAUDE.md`); skip the footer otherwise. - This skill writes ONE file. It does not commit, push, or notify. - It does not preserve background processes (those are owned by the dying instance) — it only records what was running so the new instance can re-launch them. - It does not capture full conversation history. The next instance has the harness summary plus the handoff file — that is the contract. - If the operator wants the handoff file checked into history, that is a separate explicit ask. Default is operator-local.