---
id: OBPI-0.0.65-01-canonical-location-migration
parent: ADR-0.0.65-handoff-system-consolidation
item: 1
lane: Heavy
status: Completed
---

# OBPI-0.0.65-01-canonical-location-migration: **canonical-location-migration** — Canonize `.gzkit/handoffs/` as the single handoff write location per ADR-0.0.41. Migrate the 24 per-ADR handoff files (across 10 ADR packages) into `.gzkit/handoffs/`, preserving `continues_from:` chains and frontmatter timestamps. Amend `gz-session-handoff/SKILL.md` output-path doctrine from `{ADR-package}/handoffs/` to `.gzkit/handoffs/`. Bump `skill-version` and `last_reviewed`; run `gz agent sync control-surfaces`.

## ADR Item

- **Source ADR:** `docs/design/adr/foundation/ADR-0.0.65-handoff-system-consolidation/ADR-0.0.65-handoff-system-consolidation.md`
- **Checklist Item:** #1 - "OBPI-0.0.65-01: **canonical-location-migration** — Canonize `.gzkit/handoffs/` as the single handoff write location per ADR-0.0.41. Migrate the 24 per-ADR handoff files (across 10 ADR packages) into `.gzkit/handoffs/`, preserving `continues_from:` chains and frontmatter timestamps. Amend `gz-session-handoff/SKILL.md` output-path doctrine from `{ADR-package}/handoffs/` to `.gzkit/handoffs/`. Bump `skill-version` and `last_reviewed`; run `gz agent sync control-surfaces`."

**Status:** Completed

## Objective

Migrate the 24 per-ADR session handoffs into `.gzkit/handoffs/`, preserving `continues_from:` chain integrity and frontmatter timestamps. Scope is the content migration plus deletion of the now-empty source directories; the orientation reader's dual-scan collapse is OBPI-04 scope and depends on this OBPI completing first.

### Scope Amendment (2026-05-30)

The skill canon amendment originally bundled into this OBPI landed as a direct fix earlier in the same session — `.gzkit/skills/gz-session-handoff/SKILL.md` output-path doctrine has been amended (skill-version bumped 6.3.1 → 6.4.0, `last_reviewed` bumped to 2026-05-30, mirrors regenerated via `gz agent sync control-surfaces`). This OBPI's Allowed Paths no longer include SKILL.md; the Requirements that referenced the amendment (#5, #6, #7) are preserved as non-regression assertions verified by the post-migration test pass. See the `fix(skill):` commit on this session's HEAD for the amendment evidence.

**Duplicate-source dedup (operator decision 2026-05-30).** One of the 24 legacy
sources — `docs/design/adr/pre-release/ADR-0.28.0-focused-context-loader/handoffs/20260524T181428Z-obpi-01-context-core-green.md`
— is **byte-identical** (sha256 `8bcf0c52…`) to a file already present in the
canonical store (`.gzkit/handoffs/20260524T181428Z-obpi-01-context-core-green.md`,
landed in commit `e639710a`). It is an artifact of an earlier ad-hoc partial
migration, not a distinct handoff. Per operator decision it is removed from the
source tree as a dedup (`git rm`) rather than migrated, so the canonical store
gains **23** relocated handoffs, not 24, and the distinct post-migration total is
**34** (11 pre-existing + 23 migrated), not 35. A same-name source whose content
*differed* from the canonical copy would be a fail-closed collision (REQ #9
STOP-on-BLOCKERS); only the byte-identical case is auto-deduped. The migration is
performed by `scripts/migrate_handoffs.py` (REQ #6 reusable migration logic).

## Lane

**Heavy** - This OBPI changes a command/API/schema/runtime contract surface.

> Heavy is reserved for command/API/schema/runtime-contract changes. Process,
> documentation, and template-only work stays Lite unless it changes one of
> those external surfaces.

## Allowed Paths

<!-- What files/directories are IN SCOPE? Be explicit with paths. -->

- `.gzkit/handoffs/` — destination directory for the 24 migrated handoffs (writes only — 11 pre-existing files unmodified except where they are chain-targets of migrated files)
- `docs/design/adr/foundation/*/handoffs/` — source directories (6 ADR packages: ADR-0.0.16, ADR-0.0.17, ADR-0.0.37, ADR-0.0.54, ADR-0.0.63, ADR-0.0.64 — 16 files)
- `docs/design/adr/pre-release/*/handoffs/` — source directories (4 ADR packages: ADR-0.11.0, ADR-0.18.0, ADR-0.19.0, ADR-0.27.0, ADR-0.28.0 — 8 files)
- `tests/governance/test_handoff_migration.py` — new test file asserting post-migration invariants via `@covers`-decorated tests

## Denied Paths

<!-- What files/directories are OUT OF SCOPE? Agents will not touch these. -->

- `.gzkit/skills/gz-session-handoff/SKILL.md` — already amended via direct fix on 2026-05-30 (skill-version 6.4.0); touching it here would duplicate or regress the doctrine change
- `.gzkit/skills/gz-session-handoff/assets/handoff-template.md` — sibling asset already in canonical state
- `.claude/skills/`, `.agents/skills/`, `.github/skills/`, `src/gzkit/skills/` — mirror surfaces regenerated by `gz agent sync control-surfaces`, never edited directly
- `scripts/session_orientation.py` — OBPI-0.0.65-04 scope (orientation single-location scan); editing it here is a cross-OBPI boundary violation
- `src/gzkit/handoff_api.py` — OBPI-0.0.65-02 scope (programmatic API; file doesn't exist yet)
- `src/gzkit/handoff_validation.py` — no validator-logic changes in this OBPI
- `src/gzkit/cli/**`, `src/gzkit/commands/**` — OBPI-0.0.65-03 scope (the future handoff CLI verb)
- New runtime dependencies
- CI files, lockfiles
- All other paths not enumerated under Allowed Paths

## Requirements (FAIL-CLOSED)

<!-- Constraints that MUST hold. Numbered list. NEVER/ALWAYS language.
     These are the rules agents ground against. If not met, OBPI fails. -->

1. ALWAYS: Every handoff `.md` file under `docs/design/adr/**/handoffs/` is moved into `.gzkit/handoffs/` with the original filename and frontmatter timestamp preserved verbatim.
2. ALWAYS: Every `continues_from:` frontmatter pointer that previously referenced a `docs/design/adr/**/handoffs/...` path is rewritten to the new `.gzkit/handoffs/...` location (chain integrity preserved across the migration).
3. NEVER: Leave any `.md` file behind under `docs/design/adr/**/handoffs/` after the migration runs (zero residual files).
4. ALWAYS: Empty `handoffs/` directories left under ADR packages are removed (no residual empty-dir trap for future writes).
5. ALWAYS (non-regression): `.gzkit/skills/gz-session-handoff/SKILL.md` contains zero occurrences of the literal string `{ADR-package}/handoffs/`. The amendment landed via direct fix on 2026-05-30; this REQ asserts the state is preserved through this OBPI's execution.
6. ALWAYS (non-regression): `.gzkit/skills/gz-session-handoff/SKILL.md` `skill-version` is at `6.4.0` (bumped from 6.3.1 in direct fix) and `last_reviewed` is `2026-05-30`. Re-bumping this version inside the OBPI would be a regression.
7. ALWAYS: `uv run gz agent sync control-surfaces` runs clean (no diff in mirror surfaces) after the migration completes — re-verified to confirm content migration produced no incidental skill-mirror drift.
7a. NEVER: Re-touch `.gzkit/skills/gz-session-handoff/SKILL.md` in this OBPI's pipeline execution. The amendment already landed; further edits would be duplicate work or regression.
8. NEVER: Touch `scripts/session_orientation.py`. The `_candidate_handoff_dirs()` dual-scan collapse is OBPI-0.0.65-04 scope; editing it here would collapse the firing-point boundary between briefs (per the ADR-0.0.59 increment-size safety property).
9. STOP-on-BLOCKERS: if a per-ADR source handoff carries an unresolvable `continues_from:` pointer (the target file is missing in both source and destination locations), HALT and surface the broken chain to the operator before proceeding — do not migrate a broken chain into the canonical store.
10. ALWAYS: Work stays inside Allowed Paths; verification commands run as single-program invocations (no `&&`, `||`, `|`, `;`, `$(...)`, or redirects per the OBPI-pipeline shlex contract, GHI #415).

> STOP-on-BLOCKERS: if prerequisites are missing, print a BLOCKERS list and halt.

## Discovery Checklist

<!-- What to read before implementation. Complete this checklist first.
     Order matters: read the structured input (parent ADR § Decision)
     before the unstructured one (allowed paths, prerequisites). -->

**Parent ADR (read first; order pinned — GHI #321):**

- [ ] **Parent ADR § Decision item — quote the line this OBPI implements** verbatim into the brief's Implementation Summary. The Decision item is the contract; everything else hangs off it.
- [ ] Parent ADR § Intent — the why-frame for the Decision read above.
- [ ] Parent ADR file: `docs/design/adr/foundation/ADR-0.0.65-handoff-system-consolidation/ADR-0.0.65-handoff-system-consolidation.md`

> **STOP:** If you cannot quote the parent ADR § Decision item that this OBPI implements, STOP and re-read. Do not proceed to Allowed Paths, Prerequisites, or implementation until the Decision quote is in hand.

**Governance (read once, cache):**

- [ ] `.github/discovery-index.json` - repo structure
- [ ] `AGENTS.md` or `CLAUDE.md` - agent operating contract

**Context:**

- [ ] Related OBPIs in same ADR

**Prerequisites (check existence, STOP if missing):**

- [ ] Parent ADR exists: `docs/design/adr/foundation/ADR-0.0.65-handoff-system-consolidation/ADR-0.0.65-handoff-system-consolidation.md`
- [ ] Destination directory exists: `.gzkit/handoffs/` (currently 11 files + `AGENTS.md`; will hold 34 `*.md` files + `AGENTS.md` after migration — 23 migrated, 1 source deduped)
- [ ] Source directories exist: `docs/design/adr/foundation/*/handoffs/` (6 packages, 16 files) and `docs/design/adr/pre-release/*/handoffs/` (4 packages, 8 files) — total 24 files for migration
- [ ] ADR-0.0.41 is readable: anchors the `.gzkit/handoffs/` canonical-location doctrine the operator decision honors
- [ ] `.gzkit/rules/token-block-discipline.md` is readable: line 112 names `.gzkit/handoffs/` as canonical storage per ADR-0.0.41 § token-block-discipline
- [ ] No active OBPI lock claimed on OBPI-0.0.65-01 by another agent — verified via `uv run gz obpi lock list`

**Existing Code (understand current state):**

- [ ] Read `.gzkit/skills/gz-session-handoff/SKILL.md` end-to-end (currently 291 lines) — locate every `{ADR-package}/handoffs/` reference (audit identified lines 32, 83, 248) and every secondary reference that implies the per-ADR location
- [ ] Read `.gzkit/skills/gz-session-handoff/assets/handoff-template.md` — confirm whether template references the output path explicitly
- [ ] Read `src/gzkit/content/models/handoff.py` (header line 1) — confirm whether the target-path constant already pins `.gzkit/handoffs/` and needs no model edit
- [ ] Read `scripts/session_orientation.py::_candidate_handoff_dirs` — READ ONLY (NO EDITS); confirm current dual-scan behavior so OBPI-04 has a clean before-state
- [ ] Read one chained handoff under `docs/design/adr/foundation/ADR-0.0.63-.../handoffs/` carrying `continues_from:` — establish the chain-rewrite pattern baseline
- [ ] Read one handoff already at `.gzkit/handoffs/` — confirm frontmatter shape parity (no schema drift between locations)

## Quality Gates

<!-- Which gates apply and how to verify them. -->

### Gate 1: ADR

- [ ] Intent and scope recorded in this OBPI brief
- [ ] Parent ADR checklist item quoted

### Gate 2: TDD (Red-Green-Refactor)

- [ ] Tests derived from brief acceptance criteria, not from implementation
- [ ] Red-Green-Refactor cycle followed per behavior increment
- [ ] Tests pass: `uv run gz test`
- [ ] Validation commands recorded in evidence with real outputs

### Code Quality

- [ ] Lint clean: `uv run gz lint`
- [ ] Type check clean: `uv run gz typecheck`

<!-- Heavy lane only: -->
### Gate 3: Docs (Heavy only)

- [ ] Docs build: `uv run mkdocs build --strict`
- [ ] Relevant docs updated

### Gate 4: BDD (Heavy only)

- [ ] Acceptance scenarios pass: `uv run -m behave features/`

### Gate 5: Human (Heavy only)

- [ ] Human attestation recorded

## Verification

<!-- What commands verify this work? Use real repo commands, then paste the
     outputs into Evidence. These are CONSTRUCTION HOUSEKEEPING (lint, type,
     test, mkdocs) — they prove the codebase is healthy, not what the OBPI
     yielded. The yielded product belongs in the `## Demo` section below.

     AUTHORING CONTRACT: Every command in this section must be a single-program,
     shell-less invocation — no &&, ||, |, ;, $(...), or redirects. The
     OBPI-pipeline verify stage executes commands via shlex.split + shell=False
     (GHI #415); compound commands are blocked at authoring time by
     gz validate --brief-command-shape and rejected at the verify stage.
     Write multi-step verification as separate uv run ... lines. -->

```bash
uv run gz validate --documents
uv run gz lint
uv run gz typecheck
uv run -m unittest tests.governance.test_handoff_migration
uv run gz agent sync control-surfaces
uv run gz validate --surfaces
```

## Demo

<!-- THE YIELDED PRODUCT, not housekeeping. Concrete, runnable invocations
     that demonstrate the capability this OBPI delivers — e.g. an actual
     diagnosis run against a real file, the `--json` form, an auto-chain
     trigger. The closeout ceremony walkthrough harvests this section
     (parser-validated; unregistered verbs are dropped). Prefer real paths
     and arguments over `<placeholder>` syntax. `--help` is not a demo. -->

```bash
find docs/design/adr -name "*.md" -path "*/handoffs/*"
find .gzkit/handoffs -type f -name "*.md" -not -name "AGENTS.md"
grep -n "ADR-package" .gzkit/skills/gz-session-handoff/SKILL.md
uv run python scripts/session_orientation.py
```

## Acceptance Criteria

<!--
Specific, testable criteria for completion.
Each checkbox MUST carry a deterministic REQ ID:
REQ-<semver>-<obpi_item>-<criterion_index>
-->

- [ ] REQ-0.0.65-01-01 [BEHAVIOR]: After this OBPI, zero `.md` files exist under `docs/design/adr/**/handoffs/` (the per-ADR source surface is empty). Asserted by `@covers("REQ-0.0.65-01-01")` in `tests/governance/test_handoff_migration.py` via `pathlib.Path("docs/design/adr").rglob("handoffs/*.md")` returning an empty iterator.
- [ ] REQ-0.0.65-01-02 [BEHAVIOR]: After this OBPI, exactly 34 `.md` files exist under `.gzkit/handoffs/` excluding `AGENTS.md` (11 pre-existing + 23 migrated; the 24th legacy source was a byte-identical duplicate removed as a dedup — see § Scope Amendment). Asserted by `@covers("REQ-0.0.65-01-02")` in `tests/governance/test_handoff_migration.py` via filtered glob count.
- [ ] REQ-0.0.65-01-03 [BEHAVIOR]: Every `continues_from:` frontmatter pointer in a `.gzkit/handoffs/*.md` file resolves to an existing file in `.gzkit/handoffs/` (chain integrity preserved across migration). Asserted by `@covers("REQ-0.0.65-01-03")` in `tests/governance/test_handoff_migration.py` via frontmatter parse + `Path.is_file()`.
- [ ] REQ-0.0.65-01-04 [BEHAVIOR]: `.gzkit/skills/gz-session-handoff/SKILL.md` contains zero occurrences of the literal string `{ADR-package}/handoffs/` after the amendment. Asserted by `@covers("REQ-0.0.65-01-04")` in `tests/governance/test_handoff_migration.py` via file read + substring count.
- [ ] REQ-0.0.65-01-05 [SUPPORT]: `.gzkit/skills/gz-session-handoff/SKILL.md` `skill-version` is bumped per the skill-surface-sync rule and mirror surfaces under `.claude/skills/`, `.agents/skills/`, `.github/skills/` regenerate byte-identical to canon — `gz validate --surfaces` clean + `agent_sync_completed` event emitted by `uv run gz agent sync control-surfaces`.

## Completion Checklist

<!-- Verify all gates before marking OBPI accepted. -->

- [ ] **Gate 1 (ADR):** Intent recorded in brief
- [ ] **Gate 2 (TDD):** RGR cycle followed, tests derived from brief, coverage maintained
- [ ] **Code Quality:** Lint, format, type checks clean
- [ ] **Value Narrative:** Problem-before vs capability-now is documented
- [ ] **Key Proof:** One concrete usage example is included
- [ ] **OBPI Acceptance:** Evidence recorded below

> For ceremony steps and lane-inheritance attestation rules, see `AGENTS.md` section `OBPI Acceptance Protocol`.

## Evidence

<!-- Record observations during/after implementation.
     Command outputs, file:line references, dates. -->

### Gate 1 (ADR)

- [ ] Intent and scope recorded

### Gate 2 (TDD — Red-Green-Refactor)

```text
# Paste test output here
```

### Code Quality

```text
# Paste lint/format/type check output here
```

### Gate 3 (Docs)

```text
# Paste docs-build output here when Gate 3 applies
```

### Gate 4 (BDD)

```text
# Paste behave output here when Gate 4 applies
```

### Gate 5 (Human)

```text
# Record attestation text here when required by parent lane
```

### Value Narrative

<!-- What problem existed before this OBPI, and what capability exists now? -->

### Key Proof


Command: uv run -m unittest tests.governance.test_handoff_migration -v
Output: 4 tests pass (test_no_per_adr_handoffs_remain, test_canonical_store_holds_expected_count, test_continues_from_chains_resolve, test_skill_canon_has_no_per_adr_write_path) -- Ran 4 tests, OK. Receipt arb-step-unittest-226226b6fbac46aa91e3c2c1c4d7d67d. Confirms: 0 per-ADR residual handoffs, 34 canonical handoffs, all continues_from: pointers resolve, SKILL.md free of {ADR-package}/handoffs/ literal.

### Implementation Summary


- Migration: 23 per-ADR handoffs relocated docs/design/adr/**/handoffs/ -> .gzkit/handoffs/ via git mv (rename detection preserved); 1 byte-identical duplicate (ADR-0.28.0, sha 8bcf0c52) removed via git rm per operator dedup decision
- Chain integrity: 10 continues_from: pointers rewritten from per-ADR paths to .gzkit/handoffs/ paths
- Cleanup: 9 emptied handoffs/ directories removed; zero residual .md under docs/design/adr/**/handoffs/; canonical store holds 34 distinct handoffs (11 pre-existing + 23 migrated)
- Reusable logic: scripts/migrate_handoffs.py (idempotent; fail-closed on differing-content name collision per REQ-09 STOP-on-BLOCKERS)
- Tests: tests/governance/test_handoff_migration.py (4 @covers BEHAVIOR tests, REQ-01..04)
- Gate 4: behave waiver added (filesystem migration; no Gherkin-observable runtime surface)
- Date completed: 2026-05-30
- Attestation status: operator-verbatim ("attest completed")

## Tracked Defects

<!-- Record GitHub defect linkage when defects are discovered during this OBPI.
     Use one bullet per issue so status surfaces can preserve traceability. -->

_No defects tracked._

## Human Attestation

- Attestor: `Jeffry Babb`
- Attestation: attest completed — OBPI-0.0.65-01 canonical-location-migration verified: 23 legacy per-ADR handoffs relocated to .gzkit/handoffs/ via git mv, 1 byte-identical duplicate deduped, 10 continues_from: chains rewritten, 9 empty source dirs removed; 34 canonical handoffs, zero per-ADR residuals. Full suite GREEN 5754/5754 (arb-step-unittest-b19d71ee861c4382a6d7a468c9f4424b), lint clean (arb-ruff-d202b65d48094be0b894ccaf714a0add), typecheck clean (arb-step-typecheck-b195fe0e99df4d8e90078748b5055bb6), mkdocs clean (arb-step-mkdocs-e3dea388f8d64e7c9dd74148dd793140); gz validate --documents/--surfaces clean; SUPPORT REQ-05 proven via agent_sync_completed ledger event + gz validate --surfaces. Two coupled-surface fixes landed in-flight: insights schema (evidence as list) and the defect-fix-routing fold test (exempt .gzkit/handoffs/ as historical narrative). Attestor: Jeffry Babb, 2026-05-30.
- Date: 2026-05-30

---

**Date Completed:** 2026-05-30

**Evidence Hash:** -
