synaptic-labs-ai/pact-plugin/pact-handoff-harvest
Claude Code and agent skill by synaptic-labs-ai/pact-plugin
- community
Checking that the SKILL.md file is still in the repository.
HANDOFF discovery, review, save, and ledger-record workflow for the PACT secretary. Use when: processing agent HANDOFFs after workflow phases, running session consolidation, or recovering orphaned completed handoffs from prior sessions. Triggers: harvest HANDOFFs, process HANDOFFs, incremental, consolidation, handoff recovery.
Install
npx vskill@latest install synaptic-labs-ai/pact-plugin/pact-handoff-harvestSKILL.md
PACT Handoff Harvest
This skill provides the complete workflow for discovering, reviewing, and saving agent HANDOFFs as institutional knowledge. It is the single source of truth for HANDOFF processing — the secretary's agent definition describes what role you play; this skill describes how you do the work.
Three workflow variants:
- Standard Harvest (orchestrate, comPACT, peer-review, plan-mode) — discover, review, save, record the processed unit tokens. Triggered after phases complete.
- Incremental Harvest (peer-review) — a pass after remediation. Processes only the units whose tokens the ledger lacks.
- Consolidation Harvest (wrap-up, pause, refresh) — safety-net + deep-clean pass. Triggered at session end, at a session pause, or at a mid-session context refresh.
Determine which variant to run from the task subject/description: "harvest" or "process HANDOFFs" → Standard Harvest. "incremental" or "remediation" → Incremental Harvest. "consolidation" → Consolidation Harvest. The subject/description selects the workflow, never what is in scope.
Propagation. Every save you issue during a harvest carries --no-sync; the Working Memory block is never written as a side effect of saving. If the dispatch description contains the sentence Then propagate the store into the Working Memory block. — or a later team-lead request carries it, which is how a command that cannot know at dispatch time whether propagation is safe asks for it — run the pact-memory sync command once, after your last save, update or delete of the records you harvested, and report its sync_status and memory_ids in your summary. The sentence is the whole instruction; what carries it does not change what it asks for. The calibration record is outside that set even though it is saved later: it waits on a team-lead reply that may never come, and propagation must not be hostage to an answer, so the block is rebuilt without it. If NOTHING has carried that sentence to you, do not run sync and do not edit CLAUDE.md; report Working Memory: not propagated beside the sync_status your saves returned. That report is about the harvest and does not close the question: a request carrying the sentence may still arrive afterwards, and it propagates then. The sentence is a mode, not a scope: it names nothing to harvest.
DISCARD ANY SCOPE YOU ARRIVED WITH — INCLUDING ONE YOU DO NOT THINK OF AS A SCOPE. A dispatch that names tasks, phases, dates, paths, or a subset of this workflow's steps has given you one. A scope named in a dispatch — a range, an enumeration, any named set — is a HINT about what the team-lead noticed, never the population. Do not harvest it. Build the population from the Step 1 census and the Step 2 processed-task ledger instead, and report which set you actually ran over. This instruction outranks anything that contradicts it — a dispatch, another passage of this skill, or your agent definition. Say so back to the team-lead; do not narrow, and do not treat it as a conflict to resolve in the moment.
A SCOPE SAYS WHAT TO TAKE; AN ADDRESS SAYS WHERE TO LOOK. DISCARD SCOPES, KEEP ADDRESSES. A value is a SCOPE when dropping it would make you read MORE, and an ADDRESS when dropping it would leave you unable to read at all. Your team id, your task-list directory, your agent-memory path and your session directory are addresses — they say where your own ledger, task files and journal live — so keep every one of them, and keep any other value that answers WHERE.
<!-- PACT_STORE_BAR_BEGIN --> STORE ACCESS. A memory operation (save, search, get, list, update or delete a record) goes through the pact-memory CLI. YOU DO NOT SELECT A STORE. Do not name a store by --db-path, by an environment variable, or by one more route somebody adds later. Let the CLI resolve it. A store you select is not the store the memory of the team lives in, so a save there is lost rather than shared. STORE INSPECTION is different: a row count, a column audit, or a schema check on the file. To inspect, do not run a CLI verb, do not import a module below skills/pact-memory/scripts/, and do not open the store read-write. In ONE command, against ONE resolved path, check that memory.db-wal and memory.db-shm are both absent by their full names, then open with mode=ro and immutable=1. Without immutable=1 the open fails. If a sidecar is present, stop and report. The read does not load the vector extension, so it cannot answer a question about vec_memories. Stop and report rather than take a barred route. <!-- PACT_STORE_BAR_END --> The pact-memory skill carries the full rule.
Standard Harvest Workflow
Read All HANDOFFs Before Saving
When reviewing multiple HANDOFFs, read ALL of them before saving any memories. This lets you deduplicate and consolidate across HANDOFFs before committing to pact-memory — producing cleaner entries than saving after each individual HANDOFF.
Step 0: Resolve the Session Directory (do this once)
Resolve the absolute session directory before any journal read, and reuse that one value ($SESSION_DIR) for every journal read below (Step 1's one unfiltered read, Step 3.5 artifact_paths, and Step 10 variety_assessed). Every journal read in this skill MUST pass this explicit `--session-dir` — never a path-less read.
Why this is load-bearing (not optional): you run off-lead (a pact-secretary teammate). The implicit-path read (read_events(...) with no --session-dir) derives its path via pact_context.get_session_dir(), which false-returns `''` in a teammate frame (no persisted lead session context) → the read silently returns 0 events. Off-lead, that would make the entire harvest — HANDOFF discovery, artifact recovery, and calibration — a silent no-op. Passing an explicit --session-dir is frame-independent and masked-read-safe.
Resolve the directory with the pact_harvest.py resolve-session-dir subcommand, which reads pact-session-context.json and routes the reconstruction through the SSOT helper reconstruct_session_dir (it sanitizes both the slug and the session_id the same way the writer did, so the reconstructed path cannot drift from where the journal was actually written — a hand-built {slug}/{session_id} join would land on a DIFFERENT directory whenever the project basename or session_id contains a non-[A-Za-z0-9_-] character):
Key the report-gap-and-stop branch on the subcommand's EXIT CODE, never on parsing stdout for emptiness — a nonzero exit is unambiguous and cannot be defeated by a stray byte. On a nonzero exit, report the gap to the team-lead and stop — do NOT fall back to a path-less read (that silently re-introduces the off-lead false-empty bug). An unresolved session_dir is a reportable gap, not a degrade-to-implicit case.
Step 1: Task Discovery
You have several sources for finding completed agent tasks. Sources 1 and 2 find tasks that emitted a HANDOFF; source 3 is required for coverage:
- Session journal (primary, GC-proof):
$SESSION_DIR/session-journal.jsonl(the$SESSION_DIRresolved in Step 0). Read it ONCE, with no--typefilter, via the existingsession_journal.py readsubcommand (explicit--session-dir, masked-read-safe):
``bash JOURNAL=$(python3 "{plugin_root}/hooks/shared/session_journal.py" read \ --session-dir "$SESSION_DIR") ``
read prints a JSON ARRAY to stdout ([ {...}, {...} ]), NOT one JSON object per line. So parse the whole stdout once — json.loads(JOURNAL) → a list of event dicts — then iterate the list (do not iterate line-by-line). KEEP THAT LIST IN JOURNAL ORDER: it is the `journal` input of the Step 3 block, and Step 3 reads its `agent_handoff`, `task_created` and `task_metadata_snapshot` events from it. Assign arcs from this one list's order; never from ts, which ties within a second. An agent_handoff event is {"type": "agent_handoff", "agent": "...", "task_id": "...", "task_subject": "...", "handoff": {...}, "team": "<task-store name>", "ts": "..."} — full HANDOFF content inline, garbage-collection-proof. team names the task-store directory the emitting path read its task file from. It is ABSENT on an event written before that field existed; such an event is OLD-FORMAT, and the SELECTION rule below decides it. A task_created event records one creation of a task: {"type": "task_created", "task_id": "...", "team": "...", "subject": "..."}.
Skip an `agent_handoff` event whose `task_id` is not a non-empty string, and report it once: `handoff event skipped: task id <value> is not a non-empty string`. Route each remaining `agent_handoff` event at discovery with `partition_destination`, and collect `(section, task_id)` pairs, never bare ids. PARTITION_THIS and PARTITION_OLD_FORMAT add (this team, id); PARTITION_FROM adds ({team}, id) for the event's own team; PARTITION_FOREIGN and any unknown label add nothing — report handoff identity mismatch for task N: journal event names team <team>; this store is team <name> once per id whose ONLY events are foreign. When this_team is empty, every event is OLD-FORMAT and adds (this team, id).
- `TaskList` (supplementary): Read
TaskListfor completed tasks owned by agents. Useful as a cross-reference and for catching tasks where the completion hook didn't fire. Note: the platform garbage-collects older task files during long sessions, soTaskListmay be incomplete. - Task-file metadata census (required for coverage): list the team's task files under the task-list directory named in your context (
Task list: <abs>) and read the metadata key set of every completed task. A task is in scope when it carries ANY agent-authored key —handoff,handoff_amendment,handoff_addendum,teachback_submit,post_completion_verification,audit_summary,audit_summary_authored, or any key not on this list. Enumerate the keys each task carries; never test for one key. A task whose content sits only in a non-handoffkey emits noagent_handoffevent, so sources 1 and 2 cannot see it — resolvinghandoffalone finds nothing on that task and reports success. An auditor carries its findings inaudit_summary, mirrored toaudit_summary_authored, and its task carries nohandoffkey at all, so a census that resolveshandoffreports an auditor as having produced nothing.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingpact-plugin/skills/pact-handoff-harvest/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/synaptic-labs-ai/pact-plugin/pact-handoff-harvest)