gz-session-handoff

star 7

Create and resume session handoff documents for agent context preservation across engineering sessions.

tvproductions By tvproductions schedule Updated 6/14/2026

name: gz-session-handoff persona: main-session description: Create and resume session handoff documents for agent context preservation across engineering sessions. category: agent-operations compatibility: Requires GovZero v6 framework; works with any agent operating under GovZero governance metadata: skill-version: "6.7.0" govzero-framework-version: "v6" version-consistency-rule: "Skill major version tracks GovZero major. Minor increments for governance rule changes. Patch increments for tooling/template improvements." govzero-compliance-areas: "charter (gates 1-5), lifecycle (state machine), session continuity" govzero_layer: "Layer 3 - File Sync" lifecycle_state: active owner: gzkit-governance last_reviewed: 2026-06-22 model: sonnet

gz-session-handoff (v6.7.0)

Purpose

Create and resume session handoff documents that preserve agent context across engineering sessions. When an agent pauses work on an ADR or OBPI, a handoff document captures the full state — what was done, what decisions were made, and what comes next — so that a resuming agent (or the same agent in a new session) can continue without losing context.


Trust Model

Layer 3 — File Sync: This tool creates files without verification.

  • Reads: User input, handoff template, canonical handoff directory .gzkit/handoffs/
  • Writes: Handoff markdown files under .gzkit/handoffs/ (canonical storage per ADR-0.0.41 / OBPI-0.0.41-03)
  • Validates: No placeholders, no secrets, all sections present, referenced files exist
  • Reads (RESUME only, read-only): Ledger and gz state surfaces (gz obpi status, gz obpi lock list, gz gates, gz state) to verify a handoff's claims against Layer-2 (§ Claim Verification Gate)
  • Does NOT write: Ledger files, ADR status, OBPI brief status

Inputs

Parameter Required Description
adr_id Yes ADR identifier (e.g. ADR-0.0.25)
branch Yes Current git branch (or use git branch --show-current)
agent Yes Agent identifier (e.g. claude-code, codex, copilot)
slug Yes Short descriptor for filename (e.g. create-workflow)
obpi_id No OBPI identifier if handoff is scoped to a specific brief
last_lock_event_timestamp When concluding a held lock Frontmatter key — ts of the matching obpi_lock_claimed event (Sub-Invariant 2; read by gz validate --lock-handoff-coupling)
last_commit_sha When concluding a held lock Frontmatter key — HEAD at handoff creation (git rev-parse --short HEAD)
session_id No Session identifier for tracing
continues_from No Path to previous handoff document (for chained sessions)

Outputs

  • Handoff markdown file at .gzkit/handoffs/{timestamp}-{slug}.md
  • Validation result (pass/fail with error details)
  • First next action from "Immediate Next Steps" section, surfaced as an advisory for operator review on resume (not a license to execute — see the RESUME Resume contract)

Assets

  • Handoff Template: assets/handoff-template.md (co-located with this skill)

CREATE Procedure

The CREATE workflow scaffolds a new handoff document when an agent is pausing work.

Steps

  1. Read the template from assets/handoff-template.md (co-located with this skill).

  2. Generate timestamp in ISO 8601 UTC format (e.g. 2026-02-01T10:00:00Z).

  3. Get current branch via git branch --show-current.

  4. Fill frontmatter fields:

    • mode: CREATE
    • adr_id, branch, timestamp, agent — from inputs
    • obpi_id, session_id, continues_from — from optional inputs (leave empty if not provided)
  5. Ensure the canonical handoff directory .gzkit/handoffs/ exists at the project root. Create if missing (the directory is doctrine-canonical per ADR-0.0.41 / OBPI-0.0.41-03; gz init provisions it on bootstrap but defensive creation is acceptable on a stale clone).

  6. Write the scaffold to .gzkit/handoffs/{timestamp}-{slug}.md where the timestamp is filesystem-safe (e.g. 20260201T100000Z-create-workflow.md).

  7. Populate each required section with session-specific content. The agent must replace the HTML comment guidance in each section with actual content describing the session state:

    Section Content
    Current State Summary What was done, what phase the work is in, last action status
    Important Context Architectural constraints, non-obvious dependencies, gotchas
    Decisions Made Decisions with rationale and rejected alternatives
    Immediate Next Steps Ordered list of 3-5 concrete next actions
    Pending Work / Open Loops Deferred items, blockers, discovered work
    Verification Checklist Commands and checks for the resuming agent
    Evidence / Artifacts File paths (backtick-quoted) produced during the session
  8. Validate the completed document:

    • Parse frontmatter and validate with HandoffFrontmatter model
    • No placeholder markers (TBD, TODO, FIXME, ...) in the body
    • No secrets (passwords, API keys, tokens, private keys)
    • All 7 required sections present
    • All file paths referenced in Evidence / Artifacts exist on disk
  9. Report the result:

    • File path where the handoff was written
    • Validation result (pass or list of errors)
    • First item from "Immediate Next Steps" (for quick resumption context)

Programmatic API (DESIGN TARGET — NOT YET IMPLEMENTED)

NOT IMPLEMENTED. create_handoff / scaffold_handoff / resolve_handoff_dir / generate_handoff_filename / CreateResult do not exist — tests.governance.test_session_handoff is not a real module (ModuleNotFoundError on import). Building this API is OBPI-02 of ADR-pool.handoff-system-consolidation (GHI #529). Until it lands, do CREATE by following the manual procedure above and gating the result through the validator that does exist:

from pathlib import Path
from gzkit.handoff_validation import validate_handoff_document

errors = validate_handoff_document(handoff_text, Path("."))  # [] == valid

Author with the canonical short-form frontmatter the HandoffFrontmatter model requires (adr_id: ADR-X.Y.Z, obpi_id: OBPI-X.Y.Z-NN), then run the validator BEFORE committing.

Target signature, to be built under OBPI-02:

result = create_handoff(
    adr_id="ADR-0.0.25",
    branch="feature/handoff",
    agent="claude-code",
    slug="session-end",
    sections={"Current State Summary": "All tests passing.", ...},
    obpi_id="OBPI-0.0.25-03",
    base_path=Path("."),
)

Auto-load on session start (CAP-13, GHI #326)

A SessionStart hook (.claude/settings.json for Claude Code, .codex/hooks.json for Codex CLI) runs scripts/session_orientation.py on every session boot. The orientation script's "Most-recent handoff" section selects the newest file under .gzkit/handoffs/ and classifies its age via the same Fresh / Slightly-Stale / Stale / Very-Stale buckets this skill uses. The hook output is injected as session context, so the resuming agent sees the handoff path, freshness bucket, and first-next-step before its first response without operator prompting.

The hook is the mechanical floor; this skill's RESUME workflow remains the canonical path when an operator wants the full chain traversal, branch verification, or staleness gate. Operators who do not want orientation injection can disable the hook in their local settings.

RESUME Procedure

The RESUME workflow discovers, loads, validates, and reports on existing handoff documents so a resuming agent can continue work.

Resume contract — a handoff ADVISES; it does not authorize. A handoff records a proposed plan and its context. It is NOT a clearance to execute that plan. On resume — at every freshness level, including Fresh — you MUST: (1) present the advised next steps and current state to the operator; (2) obtain explicit operator authorization before executing any of them — no file mutation, no gz ceremony, no migration until the operator says go; (3) treat the human-as-final-witness doctrine as binding from the first step — you advise; the operator rules; you note variance and stop. Barreling into execution from a handoff is the exact failure this contract exists to prevent. The plan is the destination; operator authorization is the ignition. Staleness escalates verification depth (below); it never relaxes the authorization requirement, and freshness never waives it.

Steps

  1. List available handoffs for the ADR using list_handoffs(adr_id). This scans .gzkit/handoffs/ for .md files whose adr_id: frontmatter matches, parses each frontmatter, and returns them sorted newest-first.

  2. Select a handoff — either the newest (default) or a specific file if handoff_path is provided.

  3. Classify staleness using classify_staleness(timestamp). Staleness sets how much you re-verify before presenting — it does not decide whether you need operator authorization (you always do, per the Resume contract above):

    • Fresh (< 24h): present advised steps + state; verify branch and evidence paths; await operator authorization
    • Slightly Stale (24-72h): also re-verify key assumptions before presenting
    • Stale (72h-7d): deep re-verification required; requires_human_verification flag set
    • Very Stale (> 7d): deep re-verification required; requires_human_verification set; consider re-creating the handoff
  4. Load the handoff content — read the file and parse frontmatter.

  5. Follow the handoff chain via load_handoff_chain(handoff_path) — recursively traverse continues_from links (depth limit: 20) to reconstruct session lineage from oldest ancestor to current document.

  6. Verify context using verify_context(content):

    • Check branch mismatch (handoff branch vs. current branch)
    • Re-validate referenced file paths in Evidence section
  7. Verify the handoff's claims against Layer-2 (Claim Verification Gate). Walk the Current State Summary, Decisions Made, and Immediate Next Steps; for every completion / lock / gate / readiness claim, run the matching Layer-2 check from the § Claim Verification Gate table and tag the claim VERIFIED, STALE, or UNVERIFIABLE. Verify the precondition of each advised step too — a step whose precondition is STALE is void. Never relay a handoff claim as fact without this check.

  8. Extract first next step from the "Immediate Next Steps" section using extract_first_next_step(content) — returns the text of the first numbered or bulleted item for quick resumption.

  9. Report the result, then stop and await operator authorization (do not begin executing):

    • File path of the resumed handoff
    • Staleness classification and re-verification depth applied
    • Each presented claim tagged VERIFIED / STALE / UNVERIFIABLE with its Layer-2 receipt; STALE claims and the advised steps they void called out explicitly
    • First next step, presented for operator review and authorization (not for immediate action)
    • Validation errors and context warnings
    • Chain of predecessor handoffs

Operator Authorization Gate (universal)

Every resume requires explicit operator authorization before any execution, at every freshness level — Fresh included. The agent presents the advised next steps and current state, then waits for the operator to rule. This is the human-as-final-witness doctrine applied to session resumption: the agent advises, the operator rules, the agent notes variance and stops.

Staleness escalates re-verification depth, not the authorization requirement: when staleness is Stale or Very Stale, the requires_human_verification flag is additionally set to True, signaling that the agent must deeply re-verify the handoff's assumptions (branch, evidence paths, world-state drift) before presenting — but a Fresh handoff still does not authorize execution. Freshness shortens the verification; it never converts an advisory into a license.

Claim Verification Gate (universal)

A handoff is Layer-1 narrative authorship. Every assertion it makes about completion, lock state, gate status, or "now unblocked / now satisfiable" is UNVERIFIED until checked against Layer-2 truth (the ledger and gz state). This is AGENTS.md § Behavior Rules — Never #7 applied to resume: do not read a status claim as proof of the status — read the ledger. The Operator Authorization Gate governs whether you may execute; this gate governs whether you may believe or relay what the handoff says. Both fire at every freshness level, Fresh included.

Before you present any handoff claim to the operator, and before you suggest any advised step, verify the claim — and verify the precondition of each advised step, because an advised step is only actionable while its precondition still holds:

Handoff claim shape Layer-2 check Source of truth
"OBPI complete" / "attested-complete" uv run gz obpi status <OBPI-ID>Runtime State / Completion ledger
"lock still held" / advises "release the lock" uv run gz obpi lock list lock registry
"Gate N passed" / "gates green" uv run gz gates --adr <ID> / uv run gz status ledger
any artifact-state / readiness claim uv run gz state artifact graph
"tests were green" / coverage claim re-run the canonical step (see Verification Checklist) observed output

Tag every claim you present as VERIFIED, STALE, or UNVERIFIABLE. A STALE claim voids any advised step that depends on it: surface the variance and stop — do not relay the step as actionable. Worked example (2026-06-14): a handoff asserted "OBPI lock still held (release is step 1)"; gz obpi lock list returned no active locks — the lock had already been released in a later session. Relaying "release the lock" as the next action would have acted on a claim that was false at read-time. The completion claim in the same handoff verified TRUE (gz obpi statusATTESTED COMPLETED); claims are verified individually, never trusted as a block.

Programmatic API (DESIGN TARGET — NOT YET IMPLEMENTED)

NOT IMPLEMENTED. resume_handoff / classify_staleness / extract_first_next_step / list_handoffs / load_handoff_chain / verify_context / HandoffInfo / ResumeResult / StalenessLevel do not exist — tests.governance.test_session_handoff is not a real module. Building this API is OBPI-02 of ADR-pool.handoff-system-consolidation (GHI #529). Until it lands, RESUME by reading the newest handoff under .gzkit/handoffs/ directly (filter by adr_id: frontmatter) and following its continues_from chain by hand; the SessionStart orientation (scripts/session_orientation.py) already surfaces the newest valid handoff with its freshness bucket and first next step.

Target signature, to be built under OBPI-02:

result = resume_handoff(
    adr_id="ADR-0.0.25",
    expected_branch="feature/handoff",
    base_path=Path("."),
)
# → result.staleness, result.requires_human_verification,
#   result.first_next_step, result.chain, result.is_valid

Failure Modes

Failure Cause Resolution
Template not found assets/handoff-template.md missing or path incorrect Verify skill directory structure
Canonical handoff directory missing .gzkit/handoffs/ does not exist at project root Run gz init or create the directory; the path is doctrine-canonical per ADR-0.0.41
Validation: placeholders Body contains TBD, TODO, FIXME, or ... markers Replace all placeholder text with actual content
Validation: secrets Body contains password=, api_key=, Bearer tokens, etc. Remove all secret material from the document
Validation: missing sections One or more of the 7 required sections not present Add all required section headings
Validation: missing files Evidence section references files that don't exist on disk Verify file paths or remove stale references
No handoffs found list_handoffs() returns empty for the ADR Create a handoff first using the CREATE workflow
Stale handoff Handoff age exceeds 72 hours Present to human for verification before resuming
Branch mismatch Handoff branch differs from current branch Verify with human whether branch change is intentional
Broken chain continues_from points to a non-existent file Treat current handoff as chain start; note missing predecessor

Acceptance Rules

CREATE

  • All 7 required sections populated with session-specific content (no HTML comments or placeholders remaining)
  • Frontmatter validates against HandoffFrontmatter Pydantic model
  • Full validation pipeline passes (no placeholders, no secrets, sections present, references exist)
  • File written to correct path: .gzkit/handoffs/{timestamp}-{slug}.md

RESUME

  • list_handoffs() discovers and sorts available handoffs newest-first
  • classify_staleness() correctly categorizes handoff age (Fresh / Slightly Stale / Stale / Very Stale)
  • Stale and Very Stale handoffs set requires_human_verification = True
  • extract_first_next_step() extracts the first action item for quick resumption
  • load_handoff_chain() traverses continues_from links with depth limiting and cycle detection
  • verify_context() detects branch mismatches and missing referenced files
  • resume_handoff() orchestrates the full workflow and returns a ResumeResult

Common Rationalizations

These thoughts mean STOP — you are about to lose context across the session boundary:

Thought Reality
"The handoff says the OBPI is complete, so it's done" The handoff is Layer-1 narrative; completion is a Layer-2 fact. Run gz obpi status <OBPI-ID> and read Completion before you say "done." AGENTS.md § Never #7.
"The handoff says the lock is held — I'll release it (step 1)" The lock-held claim is unverified until gz obpi lock list confirms it. If the lock was already released in a later session, "release the lock" is a void step acting on a stale precondition. Check before relaying.
"I'll just relay the handoff's next steps to the operator as the plan" Relaying a claim is asserting it. An advised step whose precondition is STALE is not a plan, it's misinformation. Tag each claim VERIFIED / STALE / UNVERIFIABLE first.
"The handoff is Fresh, so I can just start on its next steps" Freshness shortens re-verification; it never authorizes execution. The Operator Authorization Gate is universal. Present the advised steps, then wait for the operator to rule.
"The handoff is slightly stale but I remember the work" Stale handoffs trigger the human verification gate for a reason. Memory is not a substitute for explicit verification. Present to the human and wait.
"Branch mismatch is fine, I know what I'm doing" The branch field exists because branch state is part of session context. Mismatch means the world changed under the handoff. Verify with the human.
"I'll fill the placeholders in later — let me write the scaffold first" The validation gate rejects placeholders. "Later" means the next agent inherits TBD/TODO markers. Populate every section now.
"All 7 sections are overkill for a 30-minute session" The 7 sections are the minimum for context preservation. Skipping any one strands the resuming agent in exactly the place that section would have explained.
"The Evidence section references files that exist locally — close enough" Validation checks every referenced path on disk. A broken reference in a handoff is a broken handoff. Fix or remove.
"I can summarize the chain in one document instead of following continues_from" The chain is the lineage. Summarizing it loses the audit trail and the rationale that led to the current state. Traverse it.
"This work is uncommitted — I'll handoff after I commit" Handoffs preserve the in-flight state including uncommitted decisions. Commit pressure is exactly when context is most fragile. Write the handoff now.

Red Flags

  • Writing a handoff with HTML-comment placeholders still present in any section
  • Relaying a handoff's completion / lock / gate claim as fact without a Layer-2 check (gz obpi status, gz obpi lock list, gz gates, gz state)
  • Suggesting an advised step whose precondition you have not re-verified at read-time (the lock-already-released trap)
  • Executing a handoff's next steps without explicit operator authorization — at any freshness level (the Operator Authorization Gate is universal)
  • Resuming a Stale or Very Stale handoff without presenting it to the human first
  • Resuming with a branch mismatch and "I'll fix it as I go"
  • Creating a handoff that references files via prose instead of backtick-quoted paths
  • Skipping the Decisions Made section because "nothing important was decided"
  • Filling Immediate Next Steps with vague intent ("continue the work") instead of concrete actions
  • Creating a chained handoff without setting continues_from

Related Skills

Skill Relationship
gz-adr-create Creates ADR packages this handoff's adr_id: may reference (handoff storage is canonical at .gzkit/handoffs/, independent of ADR package layout)
gz-obpi-specify OBPI briefs that handoffs may reference
gz-adr-closeout-ceremony Closeout may reference handoff chain as evidence
Install via CLI
npx skills add https://github.com/tvproductions/gzkit --skill gz-session-handoff
Repository Details
star Stars 7
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
tvproductions
tvproductions Explore all skills →