Role
Maintain projectHandoff storage .handoff/(plain text):9 slots = amultidimensional handoff artifact(not just open items). Entriesonly via scripts/handoff.py CLI writes (single write entry point + machine-checked gate)。
- Dimension closure: 9 slots fixed; out-of-slot information →
unconfirmed(unconfirmed),no ad-hoc slot creation。 - Record only non-derivable items: use pointers for what is readable, record only what cannot be read — the point of handoff =avoid re-reading the repository。
- P1 no deletion / P2 rebuildable / P3 no silence(see below).
When to read which phase
| Intent | Read |
|---|---|
| Take over / continue previous work / new session enters an existing project | references/resume.md |
| Handoff / wrap-up / cross-session · cross-tool transfer | references/handoff.md |
| New project / no work storage yet | references/init.md(explicit invocation only, not an autonomous hit) |
Old model detected(HANDOFF.md / HANDOFF-ARCHIVE/ / .handoff/fp.*) |
first references/migrate.md(one-time,explicit invocation only) |
Storage (.handoff/, plain text, do not assume git)
index status summary scope exit next # 导航 + 单文件槽 + 指针
actions/<域>.jsonl pitfalls/<域>.jsonl commands/<用途>.jsonl
decisions/<id>.md unconfirmed.jsonl closed/
prev/<slot> # 单文件槽覆写前快照(自动,回读用)
- 9 slots:S1
status· S2summary· S3actions· S4pitfalls· S5confirm(runtime, not persisted) · S6decisions· S7commands· S8scope· S9exit; infrastructure:index(script-generated) /next("the single next step" pointer) /unconfirmed(unconfirmed ledger) /closed(archive) /log(usage snapshot). - Entries (JSONL, fixed key order):
{"id","created","summary",[status,blockedBy],topic?,src?,detail?}; archive lines addclosed/outcome。 id=<type><6 位>;type↔槽:t→actions /p→pitfalls /c→commands /d→decisions /u→unconfirmed /q→closed (history).Unique across all slots, never reused, script-assigned。- Decision:
decisions/<id>.md= frontmatter(id/created/status/domain/topic) + ADR body. indexrebuilt by script;never hand-written。
CLI(scripts/handoff.py,python3)
init · index · check · log · add · set · edit · rm · close · next · unconfirmed · scope · confirm · filter · view · export · import。
check= phase gate: 9 slots complete /index↔ file counts consistent / ids globally unique / date rules (created ≤ closed ≤ today, no future dates) /nextvalid;if not passed, non-zero exit, the phase must not advance。add/close= the only write entry point;dates stamped by script, ids assigned by script(the LLM has no way to fabricate them).addparameter surface split by type:handoff add action|pitfall|command|decision --…, each type accepts only fields of that type — the oldadd --slot <槽>'sunion surfacewould silently drop keys not consumed by that type; nowhard-errors and prints the new shape;editalso validates the modifiable surface by entry type (errors on mismatch).nextauto-fills:close(includingunconfirmed resolve's internal close) what was closedis exactly the currentnext→ fill the next one per policy (--no-refillcloses;handoff next --automanually triggers the same policy;next <id>can override the fill result at any time). Policydeterministic and recomputable: candidate pool = liveactions(excludingstatus=blocked); sort key(有效档, created↑, id↑); base tier[高]0 /[中]· No prefix 1 /[低]2 (prefixes also recognize full-width【高】andhigh/med/mid/medium/low, case-insensitive);time dimension= each full 30 days overdue raises one level (floor 0, so aged[低]will exceed fresh[高]). Both padding and leaving blank areprint basis(P3 is not silent). Empty pool or all blocked:closeleave blank and explain,next --autoreject and do not write pointer。- S7
commandsthin first: only collectAGENTS/READMEindoes not havenon-obvious commands + environment exceptions (derivabledo not record repeatedly)→ often empty is normal. Each gate run drops onelogsnapshot (handoff log --stats), so that later, based on data, it can be decided whether to keep that slot. set <status|summary|exit>: single-file slot write entry (addonly overwrite entry slots +decisions);single-file slot unique write path。whole-slot overwrite, no append semantics—even to change one line you must first read back the full content and then write. Two guards: reject write on empty content (--allow-emptyonly then allow); also reject write when old content >200 characters and new content <60% (--forceonly then allow, to prevent "treating whole-slot overwrite as a partial edit" from emptying the slot).snapshot precedes all guards: as long as the old slot is non-empty, no matter whether this write is allowed, rejected by the sharp-drop guard, or rejected by the empty-content guard, the old content has already landed in.handoff/prev/<slot>and can be read back.- error-correction entry:
edit <id>(change safe fields;id/created/closedcannot be changed) andrm <id>(→.handoff/trash/+ recordvoidprevents id reuse + reference guard)—errorsare not handled by hand-editing, avoidingexport→改→import --forcefull rewrite. confirm= machine-checked read-back: script sets the question, script grades the answer;notPASS= handoff incomplete. The question textis randomly drawn anew on each call—"first set the question, then answer" must carrythe same--seedboth times, otherwise the question has changed and the answer is misaligned. Spot-check questions are graded byview row text([id] topic summary, non-open entries include the trailing(status)), copy thehandoff viewrow verbatim (removing the leading-) and it is fine, do not copy only the summary.view: global view (understand the whole), default stdout;--savesave to disk (default<项目根>/handoff-view.md), belongs tothe user, non-authoritative, does notcheck。- no
python3→ ask the user to install, or degrade to minimalsh。
invariants (must be upheld in any phase)
- P1 do not delete: append only;
closedis never rewritten; removal is completed atomically only within the shutdown sequence. - P2 rebuildable:
indexis rebuilt by script;do not generate, do not persist a second copy。 - P3 not silent: each run outputs "read / written / unconfirmed」;
confirmistwo-way confirmation。
hard constraints
- Do not assume git / language / tools / layout; if needed, useProbe first。
- the sole landing point for pending semantics
.handoff/(whitelist hard contract); to-do text elsewhere iscandidate, not a pending item. - Dimension closure: 9 slots fixed, outside slots →
unconfirmed。 - source registry closure:only
scopesources registered in scope are read; unregistered → non-pending source(by definition).checkcheck validates each registrationresolvable(path exists / glob has matches);scope scanproposal only(mechanical candidate), missed scans do not affect the contract holding. - Migration precedes handoff: when an old model exists,inform the user and request migration(
init/migrateexplicit invocation only); after consent, first runmigrate(it creates storage), do not first create empty storage, do not hand off directly on the old model. - Project convention: anyphase that creates or uses storage(
init/migrate/handoff) must ensureAGENTS.mdcontains a "pending items only written to.handoff/" section (create if absent) — to prevent continuous drift. - A crash at worst produces duplicates (deduplicate by id, closed wins) —never lose。
Anti-pattern
- manually edit
.handoff/entries (bypassing the CLI → gates cannot catch it). - Manually fill in dates / choose ids.
- Write pending items outside
.handoff/(→ go throughunconfirmed,do not pretend to cover)。 - manually write a second "view / summary" as the handoff artifact (→ dual sources must drift).